From ffca21f2743a7b367fa212799c6e2fea6190dd5d Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 3 Mar 2015 16:53:59 +0100 Subject: initial commit for kodi master --- addons/library.xbmc.addon/dlfcn-win32.cpp | 263 +++ addons/library.xbmc.addon/dlfcn-win32.h | 46 + addons/library.xbmc.addon/libXBMC_addon.h | 600 +++++ addons/library.xbmc.codec/libXBMC_codec.h | 124 ++ addons/library.xbmc.gui/libXBMC_gui.h | 845 ++++++++ addons/library.xbmc.pvr/libXBMC_pvr.h | 332 +++ project/cmake/addons/CMakeLists.txt | 249 +++ project/cmake/addons/README | 68 + .../addons/audioencoder.flac/audioencoder.flac.txt | 1 + .../addons/addons/audioencoder.flac/platforms.txt | 1 + .../addons/audioencoder.lame/audioencoder.lame.txt | 1 + .../addons/addons/audioencoder.lame/platforms.txt | 1 + .../audioencoder.vorbis/audioencoder.vorbis.txt | 1 + .../addons/audioencoder.vorbis/platforms.txt | 1 + .../addons/audioencoder.wav/audioencoder.wav.txt | 1 + .../addons/addons/audioencoder.wav/platforms.txt | 1 + .../cmake/addons/addons/pvr.argustv/platforms.txt | 1 + .../addons/addons/pvr.argustv/pvr.argustv.txt | 1 + project/cmake/addons/addons/pvr.demo/platforms.txt | 1 + project/cmake/addons/addons/pvr.demo/pvr.demo.txt | 1 + .../cmake/addons/addons/pvr.dvblink/platforms.txt | 1 + .../addons/addons/pvr.dvblink/pvr.dvblink.txt | 1 + .../addons/addons/pvr.dvbviewer/platforms.txt | 1 + .../addons/addons/pvr.dvbviewer/pvr.dvbviewer.txt | 1 + project/cmake/addons/addons/pvr.hts/platforms.txt | 1 + project/cmake/addons/addons/pvr.hts/pvr.hts.txt | 1 + .../addons/addons/pvr.iptvsimple/platforms.txt | 1 + .../addons/pvr.iptvsimple/pvr.iptvsimple.txt | 1 + .../addons/pvr.mediaportal.tvserver/platforms.txt | 1 + .../pvr.mediaportal.tvserver.txt | 1 + .../cmake/addons/addons/pvr.mythtv/platforms.txt | 1 + .../cmake/addons/addons/pvr.mythtv/pvr.mythtv.txt | 1 + .../cmake/addons/addons/pvr.nextpvr/platforms.txt | 1 + .../addons/addons/pvr.nextpvr/pvr.nextpvr.txt | 1 + project/cmake/addons/addons/pvr.njoy/platforms.txt | 1 + project/cmake/addons/addons/pvr.njoy/pvr.njoy.txt | 1 + .../cmake/addons/addons/pvr.vdr.vnsi/platforms.txt | 1 + .../addons/addons/pvr.vdr.vnsi/pvr.vdr.vnsi.txt | 1 + .../cmake/addons/addons/pvr.vuplus/platforms.txt | 1 + .../cmake/addons/addons/pvr.vuplus/pvr.vuplus.txt | 1 + project/cmake/addons/addons/pvr.wmc/platforms.txt | 1 + project/cmake/addons/addons/pvr.wmc/pvr.wmc.txt | 1 + project/cmake/addons/depends/CMakeLists.txt | 42 + project/cmake/addons/depends/README | 61 + .../addons/depends/common/kodi-platform/deps.txt | 2 + .../depends/common/kodi-platform/kodi-platform.txt | 1 + .../addons/depends/common/tinyxml/CMakeLists.txt | 23 + .../addons/depends/common/tinyxml/tinyxml.txt | 1 + .../cmake/addons/depends/windows/CMakeLists.txt | 55 + .../cmake/addons/depends/windows/Find7Zip.cmake | 7 + project/cmake/addons/depends/windows/README | 19 + .../cmake/addons/depends/windows/extract-7z.cmake | 10 + .../addons/depends/windows/extract-direct.cmake | 2 + project/cmake/addons/depends/windows/install.cmake | 24 + .../cmake/addons/depends/windows/prebuilt/README | 21 + project/cmake/kodi-config.cmake.in | 11 + project/cmake/platform/android/defines.txt | 1 + project/cmake/platform/darwin/defines.txt | 1 + project/cmake/platform/freebsd/defines.txt | 1 + project/cmake/platform/ios/defines.txt | 1 + project/cmake/platform/linux/defines.txt | 1 + project/cmake/platform/rbpi/defines.txt | 1 + project/cmake/platform/windows/defines.txt | 1 + project/cmake/scripts/common/addon-helpers.cmake | 113 + project/cmake/scripts/common/addoptions.cmake | 82 + .../scripts/common/check_target_platform.cmake | 61 + project/cmake/scripts/common/handle-depends.cmake | 191 ++ project/cmake/scripts/common/prepare-env.cmake | 88 + .../cmake/scripts/windows/c-flag-overrides.cmake | 5 + .../cmake/scripts/windows/cxx-flag-overrides.cmake | 5 + project/cmake/xbmc-config.cmake.in | 4 + scripts/sync_buildenv.sh | 17 + scripts/toolchain/android-arm.cmake | 58 + scripts/toolchain/ios-arm.cmake | 58 + scripts/toolchain/linux-i486.cmake | 58 + scripts/toolchain/linux-x86_64.cmake | 58 + scripts/toolchain/osx-i386.cmake | 58 + scripts/toolchain/osx-x86_64.cmake | 58 + scripts/toolchain/rbpi-arm.cmake | 58 + version.txt | 11 + xbmc/addons/Addon.cpp | 653 ++++++ xbmc/addons/Addon.h | 278 +++ xbmc/addons/AddonCallbacks.cpp | 173 ++ xbmc/addons/AddonCallbacks.h | 466 ++++ xbmc/addons/AddonCallbacksAddon.cpp | 532 +++++ xbmc/addons/AddonCallbacksAddon.h | 73 + xbmc/addons/AddonCallbacksCodec.cpp | 116 + xbmc/addons/AddonCallbacksCodec.h | 46 + xbmc/addons/AddonCallbacksGUI.cpp | 2281 ++++++++++++++++++++ xbmc/addons/AddonCallbacksGUI.h | 272 +++ xbmc/addons/AddonCallbacksPVR.cpp | 324 +++ xbmc/addons/AddonCallbacksPVR.h | 166 ++ xbmc/addons/AddonDatabase.cpp | 797 +++++++ xbmc/addons/AddonDatabase.h | 175 ++ xbmc/addons/AddonDll.h | 572 +++++ xbmc/addons/AddonInstaller.cpp | 974 +++++++++ xbmc/addons/AddonInstaller.h | 229 ++ xbmc/addons/AddonManager.cpp | 1030 +++++++++ xbmc/addons/AddonManager.h | 253 +++ xbmc/addons/AddonStatusHandler.cpp | 175 ++ xbmc/addons/AddonStatusHandler.h | 56 + xbmc/addons/AddonVersion.cpp | 138 ++ xbmc/addons/AddonVersion.h | 72 + xbmc/addons/AudioEncoder.cpp | 90 + xbmc/addons/AudioEncoder.h | 51 + xbmc/addons/ContextItemAddon.cpp | 108 + xbmc/addons/ContextItemAddon.h | 72 + xbmc/addons/DllAddon.h | 70 + xbmc/addons/DllLibCPluff.h | 116 + xbmc/addons/DllPVRClient.h | 29 + xbmc/addons/GUIDialogAddonInfo.cpp | 458 ++++ xbmc/addons/GUIDialogAddonInfo.h | 80 + xbmc/addons/GUIDialogAddonSettings.cpp | 1200 ++++++++++ xbmc/addons/GUIDialogAddonSettings.h | 91 + xbmc/addons/GUIViewStateAddonBrowser.cpp | 132 ++ xbmc/addons/GUIViewStateAddonBrowser.h | 35 + xbmc/addons/GUIWindowAddonBrowser.cpp | 677 ++++++ xbmc/addons/GUIWindowAddonBrowser.h | 79 + xbmc/addons/IAddon.h | 138 ++ xbmc/addons/Makefile | 31 + xbmc/addons/PluginSource.cpp | 96 + xbmc/addons/PluginSource.h | 58 + xbmc/addons/Repository.cpp | 395 ++++ xbmc/addons/Repository.h | 82 + xbmc/addons/Scraper.cpp | 1033 +++++++++ xbmc/addons/Scraper.h | 178 ++ xbmc/addons/ScreenSaver.cpp | 125 ++ xbmc/addons/ScreenSaver.h | 47 + xbmc/addons/Service.cpp | 158 ++ xbmc/addons/Service.h | 63 + xbmc/addons/Skin.cpp | 491 +++++ xbmc/addons/Skin.h | 155 ++ xbmc/addons/Visualisation.cpp | 486 +++++ xbmc/addons/Visualisation.h | 111 + xbmc/addons/Webinterface.cpp | 75 + xbmc/addons/Webinterface.h | 54 + xbmc/addons/addon-bindings.mk | 20 + xbmc/addons/include/NOTE | 12 + xbmc/addons/include/xbmc_addon_cpp_dll.h | 191 ++ xbmc/addons/include/xbmc_addon_dll.h | 55 + xbmc/addons/include/xbmc_addon_types.h | 64 + xbmc/addons/include/xbmc_audioenc_dll.h | 61 + xbmc/addons/include/xbmc_audioenc_types.h | 113 + xbmc/addons/include/xbmc_codec_types.h | 55 + xbmc/addons/include/xbmc_epg_types.h | 96 + xbmc/addons/include/xbmc_pvr_dll.h | 710 ++++++ xbmc/addons/include/xbmc_pvr_types.h | 415 ++++ xbmc/addons/include/xbmc_scr_dll.h | 45 + xbmc/addons/include/xbmc_scr_types.h | 53 + xbmc/addons/include/xbmc_stream_utils.hpp | 264 +++ xbmc/addons/include/xbmc_vis_dll.h | 55 + xbmc/addons/include/xbmc_vis_types.h | 111 + xbmc/addons/test/Makefile | 9 + xbmc/addons/test/TestAddonVersion.cpp | 254 +++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp | 184 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h | 359 +++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp | 193 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h | 85 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp | 404 ++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h | 68 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp | 197 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h | 60 + .../cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp | 1767 +++++++++++++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h | 172 ++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp | 391 ++++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h | 68 + .../dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp | 493 +++++ .../dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h | 118 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h | 36 + .../dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp | 199 ++ .../dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h | 60 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp | 89 + xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h | 31 + .../cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp | 247 +++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h | 99 + .../dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp | 153 ++ .../dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h | 30 + xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in | 19 + 178 files changed, 28850 insertions(+) create mode 100644 addons/library.xbmc.addon/dlfcn-win32.cpp create mode 100644 addons/library.xbmc.addon/dlfcn-win32.h create mode 100644 addons/library.xbmc.addon/libXBMC_addon.h create mode 100644 addons/library.xbmc.codec/libXBMC_codec.h create mode 100644 addons/library.xbmc.gui/libXBMC_gui.h create mode 100644 addons/library.xbmc.pvr/libXBMC_pvr.h create mode 100644 project/cmake/addons/CMakeLists.txt create mode 100644 project/cmake/addons/README create mode 100644 project/cmake/addons/addons/audioencoder.flac/audioencoder.flac.txt create mode 100644 project/cmake/addons/addons/audioencoder.flac/platforms.txt create mode 100644 project/cmake/addons/addons/audioencoder.lame/audioencoder.lame.txt create mode 100644 project/cmake/addons/addons/audioencoder.lame/platforms.txt create mode 100644 project/cmake/addons/addons/audioencoder.vorbis/audioencoder.vorbis.txt create mode 100644 project/cmake/addons/addons/audioencoder.vorbis/platforms.txt create mode 100644 project/cmake/addons/addons/audioencoder.wav/audioencoder.wav.txt create mode 100644 project/cmake/addons/addons/audioencoder.wav/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.argustv/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.argustv/pvr.argustv.txt create mode 100644 project/cmake/addons/addons/pvr.demo/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.demo/pvr.demo.txt create mode 100644 project/cmake/addons/addons/pvr.dvblink/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.dvblink/pvr.dvblink.txt create mode 100644 project/cmake/addons/addons/pvr.dvbviewer/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.dvbviewer/pvr.dvbviewer.txt create mode 100644 project/cmake/addons/addons/pvr.hts/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.hts/pvr.hts.txt create mode 100644 project/cmake/addons/addons/pvr.iptvsimple/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.iptvsimple/pvr.iptvsimple.txt create mode 100644 project/cmake/addons/addons/pvr.mediaportal.tvserver/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.mediaportal.tvserver/pvr.mediaportal.tvserver.txt create mode 100644 project/cmake/addons/addons/pvr.mythtv/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.mythtv/pvr.mythtv.txt create mode 100644 project/cmake/addons/addons/pvr.nextpvr/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.nextpvr/pvr.nextpvr.txt create mode 100644 project/cmake/addons/addons/pvr.njoy/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.njoy/pvr.njoy.txt create mode 100644 project/cmake/addons/addons/pvr.vdr.vnsi/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.vdr.vnsi/pvr.vdr.vnsi.txt create mode 100644 project/cmake/addons/addons/pvr.vuplus/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.vuplus/pvr.vuplus.txt create mode 100644 project/cmake/addons/addons/pvr.wmc/platforms.txt create mode 100644 project/cmake/addons/addons/pvr.wmc/pvr.wmc.txt create mode 100644 project/cmake/addons/depends/CMakeLists.txt create mode 100644 project/cmake/addons/depends/README create mode 100644 project/cmake/addons/depends/common/kodi-platform/deps.txt create mode 100644 project/cmake/addons/depends/common/kodi-platform/kodi-platform.txt create mode 100644 project/cmake/addons/depends/common/tinyxml/CMakeLists.txt create mode 100644 project/cmake/addons/depends/common/tinyxml/tinyxml.txt create mode 100644 project/cmake/addons/depends/windows/CMakeLists.txt create mode 100644 project/cmake/addons/depends/windows/Find7Zip.cmake create mode 100644 project/cmake/addons/depends/windows/README create mode 100644 project/cmake/addons/depends/windows/extract-7z.cmake create mode 100644 project/cmake/addons/depends/windows/extract-direct.cmake create mode 100644 project/cmake/addons/depends/windows/install.cmake create mode 100644 project/cmake/addons/depends/windows/prebuilt/README create mode 100644 project/cmake/kodi-config.cmake.in create mode 100644 project/cmake/platform/android/defines.txt create mode 100644 project/cmake/platform/darwin/defines.txt create mode 100644 project/cmake/platform/freebsd/defines.txt create mode 100644 project/cmake/platform/ios/defines.txt create mode 100644 project/cmake/platform/linux/defines.txt create mode 100644 project/cmake/platform/rbpi/defines.txt create mode 100644 project/cmake/platform/windows/defines.txt create mode 100644 project/cmake/scripts/common/addon-helpers.cmake create mode 100644 project/cmake/scripts/common/addoptions.cmake create mode 100644 project/cmake/scripts/common/check_target_platform.cmake create mode 100644 project/cmake/scripts/common/handle-depends.cmake create mode 100644 project/cmake/scripts/common/prepare-env.cmake create mode 100644 project/cmake/scripts/windows/c-flag-overrides.cmake create mode 100644 project/cmake/scripts/windows/cxx-flag-overrides.cmake create mode 100644 project/cmake/xbmc-config.cmake.in create mode 100755 scripts/sync_buildenv.sh create mode 100644 scripts/toolchain/android-arm.cmake create mode 100644 scripts/toolchain/ios-arm.cmake create mode 100644 scripts/toolchain/linux-i486.cmake create mode 100644 scripts/toolchain/linux-x86_64.cmake create mode 100644 scripts/toolchain/osx-i386.cmake create mode 100644 scripts/toolchain/osx-x86_64.cmake create mode 100644 scripts/toolchain/rbpi-arm.cmake create mode 100644 version.txt create mode 100644 xbmc/addons/Addon.cpp create mode 100644 xbmc/addons/Addon.h create mode 100644 xbmc/addons/AddonCallbacks.cpp create mode 100644 xbmc/addons/AddonCallbacks.h create mode 100644 xbmc/addons/AddonCallbacksAddon.cpp create mode 100644 xbmc/addons/AddonCallbacksAddon.h create mode 100644 xbmc/addons/AddonCallbacksCodec.cpp create mode 100644 xbmc/addons/AddonCallbacksCodec.h create mode 100644 xbmc/addons/AddonCallbacksGUI.cpp create mode 100644 xbmc/addons/AddonCallbacksGUI.h create mode 100644 xbmc/addons/AddonCallbacksPVR.cpp create mode 100644 xbmc/addons/AddonCallbacksPVR.h create mode 100644 xbmc/addons/AddonDatabase.cpp create mode 100644 xbmc/addons/AddonDatabase.h create mode 100644 xbmc/addons/AddonDll.h create mode 100644 xbmc/addons/AddonInstaller.cpp create mode 100644 xbmc/addons/AddonInstaller.h create mode 100644 xbmc/addons/AddonManager.cpp create mode 100644 xbmc/addons/AddonManager.h create mode 100644 xbmc/addons/AddonStatusHandler.cpp create mode 100644 xbmc/addons/AddonStatusHandler.h create mode 100644 xbmc/addons/AddonVersion.cpp create mode 100644 xbmc/addons/AddonVersion.h create mode 100644 xbmc/addons/AudioEncoder.cpp create mode 100644 xbmc/addons/AudioEncoder.h create mode 100644 xbmc/addons/ContextItemAddon.cpp create mode 100644 xbmc/addons/ContextItemAddon.h create mode 100644 xbmc/addons/DllAddon.h create mode 100644 xbmc/addons/DllLibCPluff.h create mode 100644 xbmc/addons/DllPVRClient.h create mode 100644 xbmc/addons/GUIDialogAddonInfo.cpp create mode 100644 xbmc/addons/GUIDialogAddonInfo.h create mode 100644 xbmc/addons/GUIDialogAddonSettings.cpp create mode 100644 xbmc/addons/GUIDialogAddonSettings.h create mode 100644 xbmc/addons/GUIViewStateAddonBrowser.cpp create mode 100644 xbmc/addons/GUIViewStateAddonBrowser.h create mode 100644 xbmc/addons/GUIWindowAddonBrowser.cpp create mode 100644 xbmc/addons/GUIWindowAddonBrowser.h create mode 100644 xbmc/addons/IAddon.h create mode 100644 xbmc/addons/Makefile create mode 100644 xbmc/addons/PluginSource.cpp create mode 100644 xbmc/addons/PluginSource.h create mode 100644 xbmc/addons/Repository.cpp create mode 100644 xbmc/addons/Repository.h create mode 100644 xbmc/addons/Scraper.cpp create mode 100644 xbmc/addons/Scraper.h create mode 100644 xbmc/addons/ScreenSaver.cpp create mode 100644 xbmc/addons/ScreenSaver.h create mode 100644 xbmc/addons/Service.cpp create mode 100644 xbmc/addons/Service.h create mode 100644 xbmc/addons/Skin.cpp create mode 100644 xbmc/addons/Skin.h create mode 100644 xbmc/addons/Visualisation.cpp create mode 100644 xbmc/addons/Visualisation.h create mode 100644 xbmc/addons/Webinterface.cpp create mode 100644 xbmc/addons/Webinterface.h create mode 100644 xbmc/addons/addon-bindings.mk create mode 100644 xbmc/addons/include/NOTE create mode 100644 xbmc/addons/include/xbmc_addon_cpp_dll.h create mode 100644 xbmc/addons/include/xbmc_addon_dll.h create mode 100644 xbmc/addons/include/xbmc_addon_types.h create mode 100644 xbmc/addons/include/xbmc_audioenc_dll.h create mode 100644 xbmc/addons/include/xbmc_audioenc_types.h create mode 100644 xbmc/addons/include/xbmc_codec_types.h create mode 100644 xbmc/addons/include/xbmc_epg_types.h create mode 100644 xbmc/addons/include/xbmc_pvr_dll.h create mode 100644 xbmc/addons/include/xbmc_pvr_types.h create mode 100644 xbmc/addons/include/xbmc_scr_dll.h create mode 100644 xbmc/addons/include/xbmc_scr_types.h create mode 100644 xbmc/addons/include/xbmc_stream_utils.hpp create mode 100644 xbmc/addons/include/xbmc_vis_dll.h create mode 100644 xbmc/addons/include/xbmc_vis_types.h create mode 100644 xbmc/addons/test/Makefile create mode 100644 xbmc/addons/test/TestAddonVersion.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h create mode 100644 xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in diff --git a/addons/library.xbmc.addon/dlfcn-win32.cpp b/addons/library.xbmc.addon/dlfcn-win32.cpp new file mode 100644 index 0000000..5839921 --- /dev/null +++ b/addons/library.xbmc.addon/dlfcn-win32.cpp @@ -0,0 +1,263 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "dlfcn-win32.h" + +/* Note: + * MSDN says these functions are not thread-safe. We make no efforts to have + * any kind of thread safety. + */ + +/* I have no special reason to have set MAX_GLOBAL_OBJECTS to this value. Any + * comments are welcome. + */ +#define MAX_OBJECTS 255 + +static HMODULE global_objects[MAX_OBJECTS]; + +/* This function adds an object to the list of global objects. + * The implementation is very simple and slow. + * TODO: should failing this function be enough to fail the call to dlopen( )? + */ +static void global_object_add( HMODULE hModule ) +{ + int i; + + for( i = 0 ; i < MAX_OBJECTS ; i++ ) + { + if( !global_objects[i] ) + { + global_objects[i] = hModule; + break; + } + } +} + +static void global_object_rem( HMODULE hModule ) +{ + int i; + + for( i = 0 ; i < MAX_OBJECTS ; i++ ) + { + if( global_objects[i] == hModule ) + { + global_objects[i] = 0; + break; + } + } +} + +/* Argument to last function. Used in dlerror( ) */ +static char last_name[MAX_PATH]; + +static int copy_string( char *dest, int dest_size, const char *src ) +{ + int i = 0; + + if( src && dest ) + { + for( i = 0 ; i < dest_size-1 ; i++ ) + { + if( !src[i] ) + break; + else + dest[i] = src[i]; + } + } + dest[i] = '\0'; + + return i; +} + +void *dlopen( const char *file, int mode ) +{ + HMODULE hModule; + UINT uMode; + + /* Do not let Windows display the critical-error-handler message box */ + uMode = SetErrorMode( SEM_FAILCRITICALERRORS ); + + if( file == 0 ) + { + /* Save NULL pointer for error message */ + _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", file ); + + /* POSIX says that if the value of file is 0, a handle on a global + * symbol object must be provided. That object must be able to access + * all symbols from the original program file, and any objects loaded + * with the RTLD_GLOBAL flag. + * The return value from GetModuleHandle( ) allows us to retrieve + * symbols only from the original program file. For objects loaded with + * the RTLD_GLOBAL flag, we create our own list later on. + */ + hModule = GetModuleHandle( NULL ); + } + else + { + char lpFileName[MAX_PATH]; + int i; + + /* MSDN says backslashes *must* be used instead of forward slashes. */ + for( i = 0 ; i < sizeof(lpFileName)-1 ; i++ ) + { + if( !file[i] ) + break; + else if( file[i] == '/' ) + lpFileName[i] = '\\'; + else + lpFileName[i] = file[i]; + } + lpFileName[i] = '\0'; + + /* Save file name for error message */ + copy_string( last_name, sizeof(last_name), lpFileName ); + + /* POSIX says the search path is implementation-defined. + * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely + * to UNIX's search paths (start with system folders instead of current + * folder). + */ + hModule = LoadLibraryEx( (LPSTR) lpFileName, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH ); + /* If the object was loaded with RTLD_GLOBAL, add it to list of global + * objects, so that its symbols may be retrieved even if the handle for + * the original program file is passed. POSIX says that if the same + * file is specified in multiple invocations, and any of them are + * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the + * symbols will remain global. + */ + + if( hModule && (mode & RTLD_GLOBAL) ) + global_object_add( hModule ); + } + + /* Return to previous state of the error-mode bit flags. */ + SetErrorMode( uMode ); + + return (void *) hModule; +} + +int dlclose( void *handle ) +{ + HMODULE hModule = (HMODULE) handle; + BOOL ret; + + /* Save handle for error message */ + _snprintf_s( last_name, MAX_PATH, MAX_PATH, "0x%p", handle ); + + ret = FreeLibrary( hModule ); + + /* If the object was loaded with RTLD_GLOBAL, remove it from list of global + * objects. + */ + if( ret ) + global_object_rem( hModule ); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int) ret; +} + +void *dlsym( void *handle, const char *name ) +{ + FARPROC symbol; + HMODULE myhandle = (HMODULE) handle; + + /* Save symbol name for error message */ + copy_string( last_name, sizeof(last_name), name ); + + symbol = GetProcAddress( myhandle, name ); +#if 0 + if( symbol == NULL ) + { + HMODULE hModule; + + /* If the handle for the original program file is passed, also search + * in all globally loaded objects. + */ + + hModule = GetModuleHandle( NULL ); + + if( hModule == handle ) + { + int i; + + for( i = 0 ; i < MAX_OBJECTS ; i++ ) + { + if( global_objects[i] != 0 ) + { + symbol = GetProcAddress( global_objects[i], name ); + if( symbol != NULL ) + break; + } + } + } + + + CloseHandle( hModule ); + } +#endif + return (void*) symbol; +} + +char *dlerror( void ) +{ + DWORD dwMessageId; + /* POSIX says this function doesn't have to be thread-safe, so we use one + * static buffer. + * MSDN says the buffer cannot be larger than 64K bytes, so we set it to + * the limit. + */ + static char lpBuffer[65535]; + DWORD ret; + + dwMessageId = GetLastError( ); + + if( dwMessageId == 0 ) + return NULL; + + /* Format error message to: + * "": + */ + ret = copy_string( lpBuffer, sizeof(lpBuffer), "\"" ); + ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, last_name ); + ret += copy_string( lpBuffer+ret, sizeof(lpBuffer)-ret, "\": " ); + ret += FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwMessageId, + MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + lpBuffer+ret, sizeof(lpBuffer)-ret, NULL ); + + if( ret > 1 ) + { + /* POSIX says the string must not have trailing */ + if( lpBuffer[ret-2] == '\r' && lpBuffer[ret-1] == '\n' ) + lpBuffer[ret-2] = '\0'; + } + + /* POSIX says that invoking dlerror( ) a second time, immediately following + * a prior invocation, shall result in NULL being returned. + */ + SetLastError(0); + + return lpBuffer; +} + diff --git a/addons/library.xbmc.addon/dlfcn-win32.h b/addons/library.xbmc.addon/dlfcn-win32.h new file mode 100644 index 0000000..b93a029 --- /dev/null +++ b/addons/library.xbmc.addon/dlfcn-win32.h @@ -0,0 +1,46 @@ +#pragma once +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DLFCN_H +#define DLFCN_H + +/* POSIX says these are implementation-defined. + * To simplify use with Windows API, we treat them the same way. + */ + +#define RTLD_LAZY 0 +#define RTLD_NOW 0 + +#define RTLD_GLOBAL (1 << 1) +#define RTLD_LOCAL (1 << 2) + +/* These two were added in The Open Group Base Specifications Issue 6. + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. + */ + +#define RTLD_DEFAULT 0 +#define RTLD_NEXT 0 + +void *dlopen ( const char *file, int mode ); +int dlclose( void *handle ); +void *dlsym ( void *handle, const char *name ); +char *dlerror( void ); + +#endif /* DLFCN-WIN32_H */ diff --git a/addons/library.xbmc.addon/libXBMC_addon.h b/addons/library.xbmc.addon/libXBMC_addon.h new file mode 100644 index 0000000..c3ed54f --- /dev/null +++ b/addons/library.xbmc.addon/libXBMC_addon.h @@ -0,0 +1,600 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 // windows +#ifndef _SSIZE_T_DEFINED +typedef intptr_t ssize_t; +#define _SSIZE_T_DEFINED +#endif // !_SSIZE_T_DEFINED +#include "dlfcn-win32.h" +#define ADDON_DLL "\\library.xbmc.addon\\libXBMC_addon" ADDON_HELPER_EXT +#define ADDON_HELPER_EXT ".dll" +#else +#if defined(__APPLE__) // osx +#if defined(__POWERPC__) +#define ADDON_HELPER_ARCH "powerpc-osx" +#elif defined(__arm__) +#define ADDON_HELPER_ARCH "arm-osx" +#elif defined(__x86_64__) +#define ADDON_HELPER_ARCH "x86-osx" +#else +#define ADDON_HELPER_ARCH "x86-osx" +#endif +#else // linux +#if defined(__x86_64__) +#define ADDON_HELPER_ARCH "x86_64-linux" +#elif defined(_POWERPC) +#define ADDON_HELPER_ARCH "powerpc-linux" +#elif defined(_POWERPC64) +#define ADDON_HELPER_ARCH "powerpc64-linux" +#elif defined(__ARMEL__) +#define ADDON_HELPER_ARCH "arm" +#elif defined(__mips__) +#define ADDON_HELPER_ARCH "mips" +#else +#define ADDON_HELPER_ARCH "i486-linux" +#endif +#endif +#include // linux+osx +#define ADDON_HELPER_EXT ".so" +#define ADDON_DLL_NAME "libXBMC_addon-" ADDON_HELPER_ARCH ADDON_HELPER_EXT +#define ADDON_DLL "/library.xbmc.addon/" ADDON_DLL_NAME +#endif +#if defined(ANDROID) +#include +#endif + +#ifdef LOG_DEBUG +#undef LOG_DEBUG +#endif +#ifdef LOG_INFO +#undef LOG_INFO +#endif +#ifdef LOG_NOTICE +#undef LOG_NOTICE +#endif +#ifdef LOG_ERROR +#undef LOG_ERROR +#endif + +namespace ADDON +{ + typedef enum addon_log + { + LOG_DEBUG, + LOG_INFO, + LOG_NOTICE, + LOG_ERROR + } addon_log_t; + + typedef enum queue_msg + { + QUEUE_INFO, + QUEUE_WARNING, + QUEUE_ERROR + } queue_msg_t; + + class CHelper_libXBMC_addon + { + public: + CHelper_libXBMC_addon() + { + m_libXBMC_addon = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_addon() + { + if (m_libXBMC_addon) + { + XBMC_unregister_me(m_Handle, m_Callbacks); + dlclose(m_libXBMC_addon); + } + } + + bool RegisterMe(void *Handle) + { + m_Handle = Handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += ADDON_DLL; + +#if defined(ANDROID) + struct stat st; + if(stat(libBasePath.c_str(),&st) != 0) + { + std::string tempbin = getenv("XBMC_ANDROID_LIBS"); + libBasePath = tempbin + "/" + ADDON_DLL_NAME; + } +#endif + + m_libXBMC_addon = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_addon == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + XBMC_register_me = (void* (*)(void *HANDLE)) + dlsym(m_libXBMC_addon, "XBMC_register_me"); + if (XBMC_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_unregister_me = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_addon, "XBMC_unregister_me"); + if (XBMC_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_log = (void (*)(void* HANDLE, void* CB, const addon_log_t loglevel, const char *msg)) + dlsym(m_libXBMC_addon, "XBMC_log"); + if (XBMC_log == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_get_setting = (bool (*)(void* HANDLE, void* CB, const char* settingName, void *settingValue)) + dlsym(m_libXBMC_addon, "XBMC_get_setting"); + if (XBMC_get_setting == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_queue_notification = (void (*)(void* HANDLE, void* CB, const queue_msg_t loglevel, const char *msg)) + dlsym(m_libXBMC_addon, "XBMC_queue_notification"); + if (XBMC_queue_notification == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_wake_on_lan = (bool (*)(void* HANDLE, void *CB, const char *mac)) + dlsym(m_libXBMC_addon, "XBMC_wake_on_lan"); + if (XBMC_wake_on_lan == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_unknown_to_utf8 = (char* (*)(void* HANDLE, void* CB, const char* str)) + dlsym(m_libXBMC_addon, "XBMC_unknown_to_utf8"); + if (XBMC_unknown_to_utf8 == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_get_localized_string = (char* (*)(void* HANDLE, void* CB, int dwCode)) + dlsym(m_libXBMC_addon, "XBMC_get_localized_string"); + if (XBMC_get_localized_string == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_free_string = (void (*)(void* HANDLE, void* CB, char* str)) + dlsym(m_libXBMC_addon, "XBMC_free_string"); + if (XBMC_free_string == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_get_dvd_menu_language = (char* (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_addon, "XBMC_get_dvd_menu_language"); + if (XBMC_get_dvd_menu_language == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_open_file = (void* (*)(void* HANDLE, void* CB, const char* strFileName, unsigned int flags)) + dlsym(m_libXBMC_addon, "XBMC_open_file"); + if (XBMC_open_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_open_file_for_write = (void* (*)(void* HANDLE, void* CB, const char* strFileName, bool bOverWrite)) + dlsym(m_libXBMC_addon, "XBMC_open_file_for_write"); + if (XBMC_open_file_for_write == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_read_file = (ssize_t (*)(void* HANDLE, void* CB, void* file, void* lpBuf, size_t uiBufSize)) + dlsym(m_libXBMC_addon, "XBMC_read_file"); + if (XBMC_read_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_read_file_string = (bool (*)(void* HANDLE, void* CB, void* file, char *szLine, int iLineLength)) + dlsym(m_libXBMC_addon, "XBMC_read_file_string"); + if (XBMC_read_file_string == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_write_file = (ssize_t (*)(void* HANDLE, void* CB, void* file, const void* lpBuf, size_t uiBufSize)) + dlsym(m_libXBMC_addon, "XBMC_write_file"); + if (XBMC_write_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_flush_file = (void (*)(void* HANDLE, void* CB, void* file)) + dlsym(m_libXBMC_addon, "XBMC_flush_file"); + if (XBMC_flush_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_seek_file = (int64_t (*)(void* HANDLE, void* CB, void* file, int64_t iFilePosition, int iWhence)) + dlsym(m_libXBMC_addon, "XBMC_seek_file"); + if (XBMC_seek_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_truncate_file = (int (*)(void* HANDLE, void* CB, void* file, int64_t iSize)) + dlsym(m_libXBMC_addon, "XBMC_truncate_file"); + if (XBMC_truncate_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_get_file_position = (int64_t (*)(void* HANDLE, void* CB, void* file)) + dlsym(m_libXBMC_addon, "XBMC_get_file_position"); + if (XBMC_get_file_position == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_get_file_length = (int64_t (*)(void* HANDLE, void* CB, void* file)) + dlsym(m_libXBMC_addon, "XBMC_get_file_length"); + if (XBMC_get_file_length == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_close_file = (void (*)(void* HANDLE, void* CB, void* file)) + dlsym(m_libXBMC_addon, "XBMC_close_file"); + if (XBMC_close_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_get_file_chunk_size = (int (*)(void* HANDLE, void* CB, void* file)) + dlsym(m_libXBMC_addon, "XBMC_get_file_chunk_size"); + if (XBMC_get_file_chunk_size == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_file_exists = (bool (*)(void* HANDLE, void* CB, const char *strFileName, bool bUseCache)) + dlsym(m_libXBMC_addon, "XBMC_file_exists"); + if (XBMC_file_exists == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_stat_file = (int (*)(void* HANDLE, void* CB, const char *strFileName, struct __stat64* buffer)) + dlsym(m_libXBMC_addon, "XBMC_stat_file"); + if (XBMC_stat_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_delete_file = (bool (*)(void* HANDLE, void* CB, const char *strFileName)) + dlsym(m_libXBMC_addon, "XBMC_delete_file"); + if (XBMC_delete_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_can_open_directory = (bool (*)(void* HANDLE, void* CB, const char* strURL)) + dlsym(m_libXBMC_addon, "XBMC_can_open_directory"); + if (XBMC_can_open_directory == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_create_directory = (bool (*)(void* HANDLE, void* CB, const char* strPath)) + dlsym(m_libXBMC_addon, "XBMC_create_directory"); + if (XBMC_create_directory == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_directory_exists = (bool (*)(void* HANDLE, void* CB, const char* strPath)) + dlsym(m_libXBMC_addon, "XBMC_directory_exists"); + if (XBMC_directory_exists == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + XBMC_remove_directory = (bool (*)(void* HANDLE, void* CB, const char* strPath)) + dlsym(m_libXBMC_addon, "XBMC_remove_directory"); + if (XBMC_remove_directory == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + m_Callbacks = XBMC_register_me(m_Handle); + return m_Callbacks != NULL; + } + + /*! + * @brief Add a message to XBMC's log. + * @param loglevel The log level of the message. + * @param format The format of the message to pass to XBMC. + */ + void Log(const addon_log_t loglevel, const char *format, ... ) + { + char buffer[16384]; + va_list args; + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + return XBMC_log(m_Handle, m_Callbacks, loglevel, buffer); + } + + /*! + * @brief Get a settings value for this add-on. + * @param settingName The name of the setting to get. + * @param settingValue The value. + * @return True if the settings was fetched successfully, false otherwise. + */ + bool GetSetting(const char* settingName, void *settingValue) + { + return XBMC_get_setting(m_Handle, m_Callbacks, settingName, settingValue); + } + + /*! + * @brief Queue a notification in the GUI. + * @param type The message type. + * @param format The format of the message to pass to display in XBMC. + */ + void QueueNotification(const queue_msg_t type, const char *format, ... ) + { + char buffer[16384]; + va_list args; + va_start (args, format); + vsprintf (buffer, format, args); + va_end (args); + return XBMC_queue_notification(m_Handle, m_Callbacks, type, buffer); + } + + /*! + * @brief Send WakeOnLan magic packet. + * @param mac Network address of the host to wake. + * @return True if the magic packet was successfully sent, false otherwise. + */ + bool WakeOnLan(const char* mac) + { + return XBMC_wake_on_lan(m_Handle, m_Callbacks, mac); + } + + /*! + * @brief Translate a string with an unknown encoding to UTF8. + * @param str The string to translate. + * @return The string translated to UTF8. Must be freed by calling FreeString() when done. + */ + char* UnknownToUTF8(const char* str) + { + return XBMC_unknown_to_utf8(m_Handle, m_Callbacks, str); + } + + /*! + * @brief Get a localised message. + * @param dwCode The code of the message to get. + * @return The message. Must be freed by calling FreeString() when done. + */ + char* GetLocalizedString(int dwCode) + { + return XBMC_get_localized_string(m_Handle, m_Callbacks, dwCode); + } + + + /*! + * @brief Get the DVD menu language. + * @return The language. Must be freed by calling FreeString() when done. + */ + char* GetDVDMenuLanguage() + { + return XBMC_get_dvd_menu_language(m_Handle, m_Callbacks); + } + + /*! + * @brief Free the memory used by str + * @param str The string to free + */ + void FreeString(char* str) + { + return XBMC_free_string(m_Handle, m_Callbacks, str); + } + + /*! + * @brief Open the file with filename via XBMC's CFile. Needs to be closed by calling CloseFile() when done. + * @param strFileName The filename to open. + * @param flags The flags to pass. Documented in XBMC's File.h + * @return A handle for the file, or NULL if it couldn't be opened. + */ + void* OpenFile(const char* strFileName, unsigned int flags) + { + return XBMC_open_file(m_Handle, m_Callbacks, strFileName, flags); + } + + /*! + * @brief Open the file with filename via XBMC's CFile in write mode. Needs to be closed by calling CloseFile() when done. + * @param strFileName The filename to open. + * @param bOverWrite True to overwrite, false otherwise. + * @return A handle for the file, or NULL if it couldn't be opened. + */ + void* OpenFileForWrite(const char* strFileName, bool bOverWrite) + { + return XBMC_open_file_for_write(m_Handle, m_Callbacks, strFileName, bOverWrite); + } + + /*! + * @brief Read from an open file. + * @param file The file handle to read from. + * @param lpBuf The buffer to store the data in. + * @param uiBufSize The size of the buffer. + * @return number of successfully read bytes if any bytes were read and stored in + * buffer, zero if no bytes are available to read (end of file was reached) + * or undetectable error occur, -1 in case of any explicit error + */ + ssize_t ReadFile(void* file, void* lpBuf, size_t uiBufSize) + { + return XBMC_read_file(m_Handle, m_Callbacks, file, lpBuf, uiBufSize); + } + + /*! + * @brief Read a string from an open file. + * @param file The file handle to read from. + * @param szLine The buffer to store the data in. + * @param iLineLength The size of the buffer. + * @return True when a line was read, false otherwise. + */ + bool ReadFileString(void* file, char *szLine, int iLineLength) + { + return XBMC_read_file_string(m_Handle, m_Callbacks, file, szLine, iLineLength); + } + + /*! + * @brief Write to a file opened in write mode. + * @param file The file handle to write to. + * @param lpBuf The data to write. + * @param uiBufSize Size of the data to write. + * @return number of successfully written bytes if any bytes were written, + * zero if no bytes were written and no detectable error occur, + * -1 in case of any explicit error + */ + ssize_t WriteFile(void* file, const void* lpBuf, size_t uiBufSize) + { + return XBMC_write_file(m_Handle, m_Callbacks, file, lpBuf, uiBufSize); + } + + /*! + * @brief Flush buffered data. + * @param file The file handle to flush the data for. + */ + void FlushFile(void* file) + { + return XBMC_flush_file(m_Handle, m_Callbacks, file); + } + + /*! + * @brief Seek in an open file. + * @param file The file handle to see in. + * @param iFilePosition The new position. + * @param iWhence Seek argument. See stdio.h for possible values. + * @return The new position. + */ + int64_t SeekFile(void* file, int64_t iFilePosition, int iWhence) + { + return XBMC_seek_file(m_Handle, m_Callbacks, file, iFilePosition, iWhence); + } + + /*! + * @brief Truncate a file to the requested size. + * @param file The file handle to truncate. + * @param iSize The new max size. + * @return New size? + */ + int TruncateFile(void* file, int64_t iSize) + { + return XBMC_truncate_file(m_Handle, m_Callbacks, file, iSize); + } + + /*! + * @brief The current position in an open file. + * @param file The file handle to get the position for. + * @return The requested position. + */ + int64_t GetFilePosition(void* file) + { + return XBMC_get_file_position(m_Handle, m_Callbacks, file); + } + + /*! + * @brief Get the file size of an open file. + * @param file The file to get the size for. + * @return The requested size. + */ + int64_t GetFileLength(void* file) + { + return XBMC_get_file_length(m_Handle, m_Callbacks, file); + } + + /*! + * @brief Close an open file. + * @param file The file handle to close. + */ + void CloseFile(void* file) + { + return XBMC_close_file(m_Handle, m_Callbacks, file); + } + + /*! + * @brief Get the chunk size for an open file. + * @param file the file handle to get the size for. + * @return The requested size. + */ + int GetFileChunkSize(void* file) + { + return XBMC_get_file_chunk_size(m_Handle, m_Callbacks, file); + } + + /*! + * @brief Check if a file exists. + * @param strFileName The filename to check. + * @param bUseCache Check in file cache. + * @return true if the file exists false otherwise. + */ + bool FileExists(const char *strFileName, bool bUseCache) + { + return XBMC_file_exists(m_Handle, m_Callbacks, strFileName, bUseCache); + } + + /*! + * @brief Reads file status. + * @param strFileName The filename to read the status from. + * @param buffer The file status is written into this buffer. + * @return The file status was successfully read. + */ + int StatFile(const char *strFileName, struct __stat64* buffer) + { + return XBMC_stat_file(m_Handle, m_Callbacks, strFileName, buffer); + } + + /*! + * @brief Deletes a file. + * @param strFileName The filename to delete. + * @return The file was successfully deleted. + */ + bool DeleteFile(const char *strFileName) + { + return XBMC_delete_file(m_Handle, m_Callbacks, strFileName); + } + + /*! + * @brief Checks whether a directory can be opened. + * @param strUrl The URL of the directory to check. + * @return True when it can be opened, false otherwise. + */ + bool CanOpenDirectory(const char* strUrl) + { + return XBMC_can_open_directory(m_Handle, m_Callbacks, strUrl); + } + + /*! + * @brief Creates a directory. + * @param strPath Path to the directory. + * @return True when it was created, false otherwise. + */ + bool CreateDirectory(const char *strPath) + { + return XBMC_create_directory(m_Handle, m_Callbacks, strPath); + } + + /*! + * @brief Checks if a directory exists. + * @param strPath Path to the directory. + * @return True when it exists, false otherwise. + */ + bool DirectoryExists(const char *strPath) + { + return XBMC_directory_exists(m_Handle, m_Callbacks, strPath); + } + + /*! + * @brief Removes a directory. + * @param strPath Path to the directory. + * @return True when it was removed, false otherwise. + */ + bool RemoveDirectory(const char *strPath) + { + return XBMC_remove_directory(m_Handle, m_Callbacks, strPath); + } + + protected: + void* (*XBMC_register_me)(void *HANDLE); + void (*XBMC_unregister_me)(void *HANDLE, void* CB); + void (*XBMC_log)(void *HANDLE, void* CB, const addon_log_t loglevel, const char *msg); + bool (*XBMC_get_setting)(void *HANDLE, void* CB, const char* settingName, void *settingValue); + void (*XBMC_queue_notification)(void *HANDLE, void* CB, const queue_msg_t type, const char *msg); + bool (*XBMC_wake_on_lan)(void *HANDLE, void* CB, const char* mac); + char* (*XBMC_unknown_to_utf8)(void *HANDLE, void* CB, const char* str); + char* (*XBMC_get_localized_string)(void *HANDLE, void* CB, int dwCode); + char* (*XBMC_get_dvd_menu_language)(void *HANDLE, void* CB); + void (*XBMC_free_string)(void *HANDLE, void* CB, char* str); + void* (*XBMC_open_file)(void *HANDLE, void* CB, const char* strFileName, unsigned int flags); + void* (*XBMC_open_file_for_write)(void *HANDLE, void* CB, const char* strFileName, bool bOverWrite); + ssize_t (*XBMC_read_file)(void *HANDLE, void* CB, void* file, void* lpBuf, size_t uiBufSize); + bool (*XBMC_read_file_string)(void *HANDLE, void* CB, void* file, char *szLine, int iLineLength); + ssize_t(*XBMC_write_file)(void *HANDLE, void* CB, void* file, const void* lpBuf, size_t uiBufSize); + void (*XBMC_flush_file)(void *HANDLE, void* CB, void* file); + int64_t (*XBMC_seek_file)(void *HANDLE, void* CB, void* file, int64_t iFilePosition, int iWhence); + int (*XBMC_truncate_file)(void *HANDLE, void* CB, void* file, int64_t iSize); + int64_t (*XBMC_get_file_position)(void *HANDLE, void* CB, void* file); + int64_t (*XBMC_get_file_length)(void *HANDLE, void* CB, void* file); + void (*XBMC_close_file)(void *HANDLE, void* CB, void* file); + int (*XBMC_get_file_chunk_size)(void *HANDLE, void* CB, void* file); + bool (*XBMC_file_exists)(void *HANDLE, void* CB, const char *strFileName, bool bUseCache); + int (*XBMC_stat_file)(void *HANDLE, void* CB, const char *strFileName, struct __stat64* buffer); + bool (*XBMC_delete_file)(void *HANDLE, void* CB, const char *strFileName); + bool (*XBMC_can_open_directory)(void *HANDLE, void* CB, const char* strURL); + bool (*XBMC_create_directory)(void *HANDLE, void* CB, const char* strPath); + bool (*XBMC_directory_exists)(void *HANDLE, void* CB, const char* strPath); + bool (*XBMC_remove_directory)(void *HANDLE, void* CB, const char* strPath); + + private: + void *m_libXBMC_addon; + void *m_Handle; + void *m_Callbacks; + struct cb_array + { + const char* libPath; + }; + }; +}; diff --git a/addons/library.xbmc.codec/libXBMC_codec.h b/addons/library.xbmc.codec/libXBMC_codec.h new file mode 100644 index 0000000..3853f08 --- /dev/null +++ b/addons/library.xbmc.codec/libXBMC_codec.h @@ -0,0 +1,124 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include +#include +#include +#include "xbmc_codec_types.h" +#include "libXBMC_addon.h" + +#ifdef _WIN32 +#define CODEC_HELPER_DLL "\\library.xbmc.codec\\libXBMC_codec" ADDON_HELPER_EXT +#else +#define CODEC_HELPER_DLL_NAME "libXBMC_codec-" ADDON_HELPER_ARCH ADDON_HELPER_EXT +#define CODEC_HELPER_DLL "/library.xbmc.codec/" CODEC_HELPER_DLL_NAME +#endif + +class CHelper_libXBMC_codec +{ +public: + CHelper_libXBMC_codec(void) + { + m_libXBMC_codec = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_codec(void) + { + if (m_libXBMC_codec) + { + CODEC_unregister_me(m_Handle, m_Callbacks); + dlclose(m_libXBMC_codec); + } + } + + /*! + * @brief Resolve all callback methods + * @param handle Pointer to the add-on + * @return True when all methods were resolved, false otherwise. + */ + bool RegisterMe(void* handle) + { + m_Handle = handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += CODEC_HELPER_DLL; + +#if defined(ANDROID) + struct stat st; + if(stat(libBasePath.c_str(),&st) != 0) + { + std::string tempbin = getenv("XBMC_ANDROID_LIBS"); + libBasePath = tempbin + "/" + CODEC_HELPER_DLL_NAME; + } +#endif + + m_libXBMC_codec = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_codec == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + CODEC_register_me = (void* (*)(void *HANDLE)) + dlsym(m_libXBMC_codec, "CODEC_register_me"); + if (CODEC_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + CODEC_unregister_me = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_codec, "CODEC_unregister_me"); + if (CODEC_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + CODEC_get_codec_by_name = (xbmc_codec_t (*)(void* HANDLE, void* CB, const char* strCodecName)) + dlsym(m_libXBMC_codec, "CODEC_get_codec_by_name"); + if (CODEC_get_codec_by_name == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + m_Callbacks = CODEC_register_me(m_Handle); + return m_Callbacks != NULL; + } + + /*! + * @brief Get the codec id used by XBMC + * @param strCodecName The name of the codec + * @return The codec_id, or a codec_id with 0 values when not supported + */ + xbmc_codec_t GetCodecByName(const char* strCodecName) + { + return CODEC_get_codec_by_name(m_Handle, m_Callbacks, strCodecName); + } + +protected: + void* (*CODEC_register_me)(void*); + void (*CODEC_unregister_me)(void*, void*); + xbmc_codec_t (*CODEC_get_codec_by_name)(void *HANDLE, void* CB, const char* strCodecName); + +private: + void* m_libXBMC_codec; + void* m_Handle; + void* m_Callbacks; + struct cb_array + { + const char* libPath; + }; +}; + diff --git a/addons/library.xbmc.gui/libXBMC_gui.h b/addons/library.xbmc.gui/libXBMC_gui.h new file mode 100644 index 0000000..8dbc38b --- /dev/null +++ b/addons/library.xbmc.gui/libXBMC_gui.h @@ -0,0 +1,845 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include +#include +#include +#include "libXBMC_addon.h" + +typedef void* GUIHANDLE; + +#ifdef _WIN32 +#define GUI_HELPER_DLL "\\library.xbmc.gui\\libXBMC_gui" ADDON_HELPER_EXT +#else +#define GUI_HELPER_DLL_NAME "libXBMC_gui-" ADDON_HELPER_ARCH ADDON_HELPER_EXT +#define GUI_HELPER_DLL "/library.xbmc.gui/" GUI_HELPER_DLL_NAME +#endif + +/* current ADDONGUI API version */ +#define XBMC_GUI_API_VERSION "5.8.0" + +/* min. ADDONGUI API version */ +#define XBMC_GUI_MIN_API_VERSION "5.8.0" + +#define ADDON_ACTION_PREVIOUS_MENU 10 +#define ADDON_ACTION_CLOSE_DIALOG 51 +#define ADDON_ACTION_NAV_BACK 92 + +class CAddonGUIWindow; +class CAddonGUISpinControl; +class CAddonGUIRadioButton; +class CAddonGUIProgressControl; +class CAddonListItem; +class CAddonGUIRenderingControl; +class CAddonGUISliderControl; +class CAddonGUISettingsSliderControl; + +class CHelper_libXBMC_gui +{ +public: + CHelper_libXBMC_gui() + { + m_libXBMC_gui = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_gui() + { + if (m_libXBMC_gui) + { + GUI_unregister_me(m_Handle, m_Callbacks); + dlclose(m_libXBMC_gui); + } + } + + bool RegisterMe(void *Handle) + { + m_Handle = Handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += GUI_HELPER_DLL; + +#if defined(ANDROID) + struct stat st; + if(stat(libBasePath.c_str(),&st) != 0) + { + std::string tempbin = getenv("XBMC_ANDROID_LIBS"); + libBasePath = tempbin + "/" + GUI_HELPER_DLL_NAME; + } +#endif + + m_libXBMC_gui = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_gui == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + GUI_register_me = (void* (*)(void *HANDLE)) + dlsym(m_libXBMC_gui, "GUI_register_me"); + if (GUI_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_unregister_me = (void (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_unregister_me"); + if (GUI_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_lock = (void (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_lock"); + if (GUI_lock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_unlock = (void (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_unlock"); + if (GUI_unlock == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_get_screen_height = (int (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_get_screen_height"); + if (GUI_get_screen_height == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_get_screen_width = (int (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_get_screen_width"); + if (GUI_get_screen_width == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_get_video_resolution = (int (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_get_video_resolution"); + if (GUI_get_video_resolution == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_Window_create = (CAddonGUIWindow* (*)(void *HANDLE, void *CB, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog)) + dlsym(m_libXBMC_gui, "GUI_Window_create"); + if (GUI_Window_create == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_Window_destroy = (void (*)(CAddonGUIWindow* p)) + dlsym(m_libXBMC_gui, "GUI_Window_destroy"); + if (GUI_Window_destroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_get_spin = (CAddonGUISpinControl* (*)(void *HANDLE, void *CB, CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_spin"); + if (GUI_control_get_spin == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_release_spin = (void (*)(CAddonGUISpinControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_spin"); + if (GUI_control_release_spin == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_get_radiobutton = (CAddonGUIRadioButton* (*)(void *HANDLE, void *CB, CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_radiobutton"); + if (GUI_control_get_radiobutton == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_release_radiobutton = (void (*)(CAddonGUIRadioButton* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_radiobutton"); + if (GUI_control_release_radiobutton == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_get_progress = (CAddonGUIProgressControl* (*)(void *HANDLE, void *CB, CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_progress"); + if (GUI_control_get_progress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_release_progress = (void (*)(CAddonGUIProgressControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_progress"); + if (GUI_control_release_progress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_ListItem_create = (CAddonListItem* (*)(void *HANDLE, void *CB, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path)) + dlsym(m_libXBMC_gui, "GUI_ListItem_create"); + if (GUI_ListItem_create == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_ListItem_destroy = (void (*)(CAddonListItem* p)) + dlsym(m_libXBMC_gui, "GUI_ListItem_destroy"); + if (GUI_ListItem_destroy == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_get_rendering = (CAddonGUIRenderingControl* (*)(void *HANDLE, void *CB, CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_rendering"); + if (GUI_control_get_rendering == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_release_rendering = (void (*)(CAddonGUIRenderingControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_rendering"); + if (GUI_control_release_rendering == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_get_slider = (CAddonGUISliderControl* (*)(void *HANDLE, void *CB, CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_slider"); + if (GUI_control_get_slider == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_release_slider = (void (*)(CAddonGUISliderControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_slider"); + if (GUI_control_release_slider == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_get_settings_slider = (CAddonGUISettingsSliderControl* (*)(void *HANDLE, void *CB, CAddonGUIWindow *window, int controlId)) + dlsym(m_libXBMC_gui, "GUI_control_get_settings_slider"); + if (GUI_control_get_settings_slider == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_control_release_settings_slider = (void (*)(CAddonGUISettingsSliderControl* p)) + dlsym(m_libXBMC_gui, "GUI_control_release_settings_slider"); + if (GUI_control_release_settings_slider == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_get_input_with_head = (bool (*)(void *HANDLE, void *CB, char &aTextString, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_get_input_with_head"); + if (GUI_dialog_keyboard_show_and_get_input_with_head == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_get_input = (bool (*)(void *HANDLE, void *CB, char &aTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_get_input"); + if (GUI_dialog_keyboard_show_and_get_input == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_get_new_password_with_head = (bool (*)(void *HANDLE, void *CB, char &newPassword, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_get_new_password_with_head"); + if (GUI_dialog_keyboard_show_and_get_new_password_with_head == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_get_new_password = (bool (*)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_get_new_password"); + if (GUI_dialog_keyboard_show_and_get_new_password == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_verify_new_password_with_head = (bool (*)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_verify_new_password_with_head"); + if (GUI_dialog_keyboard_show_and_verify_new_password_with_head == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_verify_new_password = (bool (*)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_verify_new_password"); + if (GUI_dialog_keyboard_show_and_verify_new_password == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_verify_password = (int (*)(void *HANDLE, void *CB, char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_verify_password"); + if (GUI_dialog_keyboard_show_and_verify_password == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_show_and_get_filter = (bool (*)(void *HANDLE, void *CB, char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_show_and_get_filter"); + if (GUI_dialog_keyboard_show_and_get_filter == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_send_text_to_active_keyboard = (bool (*)(void *HANDLE, void *CB, const char *aTextString, bool closeKeyboard)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_send_text_to_active_keyboard"); + if (GUI_dialog_keyboard_send_text_to_active_keyboard == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_keyboard_is_activated = (bool (*)(void *HANDLE, void *CB)) + dlsym(m_libXBMC_gui, "GUI_dialog_keyboard_is_activated"); + if (GUI_dialog_keyboard_is_activated == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_verify_new_password = (bool (*)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_verify_new_password"); + if (GUI_dialog_numeric_show_and_verify_new_password == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_verify_password = (int (*)(void *HANDLE, void *CB, char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_verify_password"); + if (GUI_dialog_numeric_show_and_verify_password == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_verify_input = (bool (*)(void *HANDLE, void *CB, char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_verify_input"); + if (GUI_dialog_numeric_show_and_verify_input == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_get_time = (bool (*)(void *HANDLE, void *CB, tm &time, const char *strHeading)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_get_time"); + if (GUI_dialog_numeric_show_and_get_time == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_get_date = (bool (*)(void *HANDLE, void *CB, tm &date, const char *strHeading)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_get_date"); + if (GUI_dialog_numeric_show_and_get_date == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_get_ipaddress = (bool (*)(void *HANDLE, void *CB, char &IPAddress, unsigned int iMaxStringSize, const char *strHeading)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_get_ipaddress"); + if (GUI_dialog_numeric_show_and_get_ipaddress == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_get_number = (bool (*)(void *HANDLE, void *CB, char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_get_number"); + if (GUI_dialog_numeric_show_and_get_number == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_numeric_show_and_get_seconds = (bool (*)(void *HANDLE, void *CB, char &strTime, unsigned int iMaxStringSize, const char *strHeading)) + dlsym(m_libXBMC_gui, "GUI_dialog_numeric_show_and_get_seconds"); + if (GUI_dialog_numeric_show_and_get_seconds == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_filebrowser_show_and_get_file = (bool (*)(void *HANDLE, void *CB, const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList)) + dlsym(m_libXBMC_gui, "GUI_dialog_filebrowser_show_and_get_file"); + if (GUI_dialog_filebrowser_show_and_get_file == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_ok_show_and_get_input_single_text = (void (*)(void *HANDLE, void *CB, const char *heading, const char *text)) + dlsym(m_libXBMC_gui, "GUI_dialog_ok_show_and_get_input_single_text"); + if (GUI_dialog_ok_show_and_get_input_single_text == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_ok_show_and_get_input_line_text = (void (*)(void *HANDLE, void *CB, const char *heading, const char *line0, const char *line1, const char *line2)) + dlsym(m_libXBMC_gui, "GUI_dialog_ok_show_and_get_input_line_text"); + if (GUI_dialog_ok_show_and_get_input_line_text == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_yesno_show_and_get_input_singletext = (bool (*)(void *HANDLE, void *CB, const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel)) + dlsym(m_libXBMC_gui, "GUI_dialog_yesno_show_and_get_input_singletext"); + if (GUI_dialog_yesno_show_and_get_input_singletext == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_yesno_show_and_get_input_linetext = (bool (*)(void *HANDLE, void *CB, const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel)) + dlsym(m_libXBMC_gui, "GUI_dialog_yesno_show_and_get_input_linetext"); + if (GUI_dialog_yesno_show_and_get_input_linetext == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_yesno_show_and_get_input_linebuttontext = (bool (*)(void *HANDLE, void *CB, const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel)) + dlsym(m_libXBMC_gui, "GUI_dialog_yesno_show_and_get_input_linebuttontext"); + if (GUI_dialog_yesno_show_and_get_input_linebuttontext == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_text_viewer = (void (*)(void *hdl, void *cb, const char *heading, const char *text)) + dlsym(m_libXBMC_gui, "GUI_dialog_text_viewer"); + if (GUI_dialog_text_viewer == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + GUI_dialog_select = (int (*)(void *hdl, void *cb, const char *heading, const char *entries[], unsigned int size, int selected)) + dlsym(m_libXBMC_gui, "GUI_dialog_select"); + if (GUI_dialog_select == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + m_Callbacks = GUI_register_me(m_Handle); + return m_Callbacks != NULL; + } + + void Lock() + { + return GUI_lock(m_Handle, m_Callbacks); + } + + void Unlock() + { + return GUI_unlock(m_Handle, m_Callbacks); + } + + int GetScreenHeight() + { + return GUI_get_screen_height(m_Handle, m_Callbacks); + } + + int GetScreenWidth() + { + return GUI_get_screen_width(m_Handle, m_Callbacks); + } + + int GetVideoResolution() + { + return GUI_get_video_resolution(m_Handle, m_Callbacks); + } + + CAddonGUIWindow* Window_create(const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog) + { + return GUI_Window_create(m_Handle, m_Callbacks, xmlFilename, defaultSkin, forceFallback, asDialog); + } + + void Window_destroy(CAddonGUIWindow* p) + { + return GUI_Window_destroy(p); + } + + CAddonGUISpinControl* Control_getSpin(CAddonGUIWindow *window, int controlId) + { + return GUI_control_get_spin(m_Handle, m_Callbacks, window, controlId); + } + + void Control_releaseSpin(CAddonGUISpinControl* p) + { + return GUI_control_release_spin(p); + } + + CAddonGUIRadioButton* Control_getRadioButton(CAddonGUIWindow *window, int controlId) + { + return GUI_control_get_radiobutton(m_Handle, m_Callbacks, window, controlId); + } + + void Control_releaseRadioButton(CAddonGUIRadioButton* p) + { + return GUI_control_release_radiobutton(p); + } + + CAddonGUIProgressControl* Control_getProgress(CAddonGUIWindow *window, int controlId) + { + return GUI_control_get_progress(m_Handle, m_Callbacks, window, controlId); + } + + void Control_releaseProgress(CAddonGUIProgressControl* p) + { + return GUI_control_release_progress(p); + } + + CAddonListItem* ListItem_create(const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path) + { + return GUI_ListItem_create(m_Handle, m_Callbacks, label, label2, iconImage, thumbnailImage, path); + } + + void ListItem_destroy(CAddonListItem* p) + { + return GUI_ListItem_destroy(p); + } + + CAddonGUIRenderingControl* Control_getRendering(CAddonGUIWindow *window, int controlId) + { + return GUI_control_get_rendering(m_Handle, m_Callbacks, window, controlId); + } + + void Control_releaseRendering(CAddonGUIRenderingControl* p) + { + return GUI_control_release_rendering(p); + } + + CAddonGUISliderControl* Control_getSlider(CAddonGUIWindow *window, int controlId) + { + return GUI_control_get_slider(m_Handle, m_Callbacks, window, controlId); + } + + void Control_releaseSlider(CAddonGUISliderControl* p) + { + return GUI_control_release_slider(p); + } + + CAddonGUISettingsSliderControl* Control_getSettingsSlider(CAddonGUIWindow *window, int controlId) + { + return GUI_control_get_settings_slider(m_Handle, m_Callbacks, window, controlId); + } + + void Control_releaseSettingsSlider(CAddonGUISettingsSliderControl* p) + { + return GUI_control_release_settings_slider(p); + } + + /*! @name GUI Keyboard functions */ + //@{ + bool Dialog_Keyboard_ShowAndGetInput(char &strText, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_get_input_with_head(m_Handle, m_Callbacks, strText, iMaxStringSize, strHeading, allowEmptyResult, hiddenInput, autoCloseMs); + } + + bool Dialog_Keyboard_ShowAndGetInput(char &strText, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_get_input(m_Handle, m_Callbacks, strText, iMaxStringSize, allowEmptyResult, autoCloseMs); + } + + bool Dialog_Keyboard_ShowAndGetNewPassword(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_get_new_password_with_head(m_Handle, m_Callbacks, strNewPassword, iMaxStringSize, strHeading, allowEmptyResult, autoCloseMs); + } + + bool Dialog_Keyboard_ShowAndGetNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_get_new_password(m_Handle, m_Callbacks, strNewPassword, iMaxStringSize, autoCloseMs); + } + + bool Dialog_Keyboard_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_verify_new_password_with_head(m_Handle, m_Callbacks, strNewPassword, iMaxStringSize, strHeading, allowEmptyResult, autoCloseMs); + } + + bool Dialog_Keyboard_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_verify_new_password(m_Handle, m_Callbacks, strNewPassword, iMaxStringSize, autoCloseMs); + } + + int Dialog_Keyboard_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_verify_password(m_Handle, m_Callbacks, strPassword, iMaxStringSize, strHeading, iRetries, autoCloseMs); + } + + bool Dialog_Keyboard_ShowAndGetFilter(char &strText, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs = 0) + { + return GUI_dialog_keyboard_show_and_get_filter(m_Handle, m_Callbacks, strText, iMaxStringSize, searching, autoCloseMs); + } + + bool Dialog_Keyboard_SendTextToActiveKeyboard(const char *aTextString, bool closeKeyboard = false) + { + return GUI_dialog_keyboard_send_text_to_active_keyboard(m_Handle, m_Callbacks, aTextString, closeKeyboard); + } + + bool Dialog_Keyboard_isKeyboardActivated() + { + return GUI_dialog_keyboard_is_activated(m_Handle, m_Callbacks); + } + //@} + + /*! @name GUI Numeric functions */ + //@{ + bool Dialog_Numeric_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize) + { + return GUI_dialog_numeric_show_and_verify_new_password(m_Handle, m_Callbacks, strNewPassword, iMaxStringSize); + } + + int Dialog_Numeric_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries) + { + return GUI_dialog_numeric_show_and_verify_password(m_Handle, m_Callbacks, strPassword, iMaxStringSize, strHeading, iRetries); + } + + bool Dialog_Numeric_ShowAndVerifyInput(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput) + { + return GUI_dialog_numeric_show_and_verify_input(m_Handle, m_Callbacks, strPassword, iMaxStringSize, strHeading, bGetUserInput); + } + + bool Dialog_Numeric_ShowAndGetTime(tm &time, const char *strHeading) + { + return GUI_dialog_numeric_show_and_get_time(m_Handle, m_Callbacks, time, strHeading); + } + + bool Dialog_Numeric_ShowAndGetDate(tm &date, const char *strHeading) + { + return GUI_dialog_numeric_show_and_get_date(m_Handle, m_Callbacks, date, strHeading); + } + + bool Dialog_Numeric_ShowAndGetIPAddress(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading) + { + return GUI_dialog_numeric_show_and_get_ipaddress(m_Handle, m_Callbacks, strIPAddress, iMaxStringSize, strHeading); + } + + bool Dialog_Numeric_ShowAndGetNumber(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs = 0) + { + return GUI_dialog_numeric_show_and_get_number(m_Handle, m_Callbacks, strInput, iMaxStringSize, strHeading, iAutoCloseTimeoutMs = 0); + } + + bool Dialog_Numeric_ShowAndGetSeconds(char &strTime, unsigned int iMaxStringSize, const char *strHeading) + { + return GUI_dialog_numeric_show_and_get_seconds(m_Handle, m_Callbacks, strTime, iMaxStringSize, strHeading); + } + //@} + + /*! @name GUI File browser functions */ + //@{ + bool Dialog_FileBrowser_ShowAndGetFile(const char *directory, const char *mask, const char *heading, char &strPath, unsigned int iMaxStringSize, bool useThumbs = false, bool useFileDirectories = false, bool singleList = false) + { + return GUI_dialog_filebrowser_show_and_get_file(m_Handle, m_Callbacks, directory, mask, heading, strPath, iMaxStringSize, useThumbs, useFileDirectories, singleList); + } + //@} + + /*! @name GUI OK Dialog functions */ + //@{ + void Dialog_OK_ShowAndGetInput(const char *heading, const char *text) + { + GUI_dialog_ok_show_and_get_input_single_text(m_Handle, m_Callbacks, heading, text); + } + + void Dialog_OK_ShowAndGetInput(const char *heading, const char *line0, const char *line1, const char *line2) + { + GUI_dialog_ok_show_and_get_input_line_text(m_Handle, m_Callbacks, heading, line0, line1, line2); + } + //@} + + /*! @name GUI Yes No Dialog functions */ + //@{ + bool Dialog_YesNo_ShowAndGetInput(const char *heading, const char *text, bool& bCanceled, const char *noLabel = "", const char *yesLabel = "") + { + return GUI_dialog_yesno_show_and_get_input_singletext(m_Handle, m_Callbacks, heading, text, bCanceled, noLabel, yesLabel); + } + + bool Dialog_YesNo_ShowAndGetInput(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel = "", const char *yesLabel = "") + { + return GUI_dialog_yesno_show_and_get_input_linetext(m_Handle, m_Callbacks, heading, line0, line1, line2, noLabel, yesLabel); + } + + bool Dialog_YesNo_ShowAndGetInput(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel = "", const char *yesLabel = "") + { + return GUI_dialog_yesno_show_and_get_input_linebuttontext(m_Handle, m_Callbacks, heading, line0, line1, line2, bCanceled, noLabel, yesLabel); + } + //@} + + /*! @name GUI Text viewer Dialog */ + //@{ + void Dialog_TextViewer(const char *heading, const char *text) + { + return GUI_dialog_text_viewer(m_Handle, m_Callbacks, heading, text); + } + //@} + + /*! @name GUI select Dialog */ + //@{ + int Dialog_Select(const char *heading, const char *entries[], unsigned int size, int selected = -1) + { + return GUI_dialog_select(m_Handle, m_Callbacks, heading, entries, size, selected); + } + //@} + +protected: + void* (*GUI_register_me)(void *HANDLE); + void (*GUI_unregister_me)(void *HANDLE, void* CB); + void (*GUI_lock)(void *HANDLE, void* CB); + void (*GUI_unlock)(void *HANDLE, void* CB); + int (*GUI_get_screen_height)(void *HANDLE, void* CB); + int (*GUI_get_screen_width)(void *HANDLE, void* CB); + int (*GUI_get_video_resolution)(void *HANDLE, void* CB); + CAddonGUIWindow* (*GUI_Window_create)(void *HANDLE, void* CB, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + void (*GUI_Window_destroy)(CAddonGUIWindow* p); + CAddonGUISpinControl* (*GUI_control_get_spin)(void *HANDLE, void* CB, CAddonGUIWindow *window, int controlId); + void (*GUI_control_release_spin)(CAddonGUISpinControl* p); + CAddonGUIRadioButton* (*GUI_control_get_radiobutton)(void *HANDLE, void* CB, CAddonGUIWindow *window, int controlId); + void (*GUI_control_release_radiobutton)(CAddonGUIRadioButton* p); + CAddonGUIProgressControl* (*GUI_control_get_progress)(void *HANDLE, void* CB, CAddonGUIWindow *window, int controlId); + void (*GUI_control_release_progress)(CAddonGUIProgressControl* p); + CAddonListItem* (*GUI_ListItem_create)(void *HANDLE, void* CB, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + void (*GUI_ListItem_destroy)(CAddonListItem* p); + CAddonGUIRenderingControl* (*GUI_control_get_rendering)(void *HANDLE, void* CB, CAddonGUIWindow *window, int controlId); + void (*GUI_control_release_rendering)(CAddonGUIRenderingControl* p); + CAddonGUISliderControl* (*GUI_control_get_slider)(void *HANDLE, void* CB, CAddonGUIWindow *window, int controlId); + void (*GUI_control_release_slider)(CAddonGUISliderControl* p); + CAddonGUISettingsSliderControl* (*GUI_control_get_settings_slider)(void *HANDLE, void* CB, CAddonGUIWindow *window, int controlId); + void (*GUI_control_release_settings_slider)(CAddonGUISettingsSliderControl* p); + bool (*GUI_dialog_keyboard_show_and_get_input_with_head)(void *HANDLE, void *CB, char &aTextString, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_show_and_get_input)(void *HANDLE, void *CB, char &aTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_show_and_get_new_password_with_head)(void *HANDLE, void *CB, char &newPassword, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_show_and_get_new_password)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_show_and_verify_new_password_with_head)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_show_and_verify_new_password)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); + int (*GUI_dialog_keyboard_show_and_verify_password)(void *HANDLE, void *CB, char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_show_and_get_filter)(void *HANDLE, void *CB, char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs); + bool (*GUI_dialog_keyboard_send_text_to_active_keyboard)(void *HANDLE, void *CB, const char *aTextString, bool closeKeyboard); + bool (*GUI_dialog_keyboard_is_activated)(void *HANDLE, void *CB); + bool (*GUI_dialog_numeric_show_and_verify_new_password)(void *HANDLE, void *CB, char &strNewPassword, unsigned int iMaxStringSize); + int (*GUI_dialog_numeric_show_and_verify_password)(void *HANDLE, void *CB, char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries); + bool (*GUI_dialog_numeric_show_and_verify_input)(void *HANDLE, void *CB, char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput); + bool (*GUI_dialog_numeric_show_and_get_time)(void *HANDLE, void *CB, tm &time, const char *strHeading); + bool (*GUI_dialog_numeric_show_and_get_date)(void *HANDLE, void *CB, tm &date, const char *strHeading); + bool (*GUI_dialog_numeric_show_and_get_ipaddress)(void *HANDLE, void *CB, char &IPAddress, unsigned int iMaxStringSize, const char *strHeading); + bool (*GUI_dialog_numeric_show_and_get_number)(void *HANDLE, void *CB, char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs); + bool (*GUI_dialog_numeric_show_and_get_seconds)(void *HANDLE, void *CB, char &strTime, unsigned int iMaxStringSize, const char *strHeading); + bool (*GUI_dialog_filebrowser_show_and_get_file)(void *HANDLE, void *CB, const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList); + void (*GUI_dialog_ok_show_and_get_input_single_text)(void *HANDLE, void *CB, const char *heading, const char *text); + void (*GUI_dialog_ok_show_and_get_input_line_text)(void *HANDLE, void *CB, const char *heading, const char *line0, const char *line1, const char *line2); + bool (*GUI_dialog_yesno_show_and_get_input_singletext)(void *HANDLE, void *CB, const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel); + bool (*GUI_dialog_yesno_show_and_get_input_linetext)(void *HANDLE, void *CB, const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel); + bool (*GUI_dialog_yesno_show_and_get_input_linebuttontext)(void *HANDLE, void *CB, const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel); + void (*GUI_dialog_text_viewer)(void *hdl, void *cb, const char *heading, const char *text); + int (*GUI_dialog_select)(void *hdl, void *cb, const char *heading, const char *entries[], unsigned int size, int selected); + +private: + void *m_libXBMC_gui; + void *m_Handle; + void *m_Callbacks; + struct cb_array + { + const char* libPath; + }; +}; + +class CAddonGUISpinControl +{ +public: + CAddonGUISpinControl(void *hdl, void *cb, CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUISpinControl(void) {} + + virtual void SetVisible(bool yesNo); + virtual void SetText(const char *label); + virtual void Clear(); + virtual void AddLabel(const char *label, int iValue); + virtual int GetValue(); + virtual void SetValue(int iValue); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_SpinHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonGUIRadioButton +{ +public: + CAddonGUIRadioButton(void *hdl, void *cb, CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUIRadioButton() {} + + virtual void SetVisible(bool yesNo); + virtual void SetText(const char *label); + virtual void SetSelected(bool yesNo); + virtual bool IsSelected(); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_ButtonHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonGUIProgressControl +{ +public: + CAddonGUIProgressControl(void *hdl, void *cb, CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUIProgressControl(void) {} + + virtual void SetPercentage(float fPercent); + virtual float GetPercentage() const; + virtual void SetInfo(int iInfo); + virtual int GetInfo() const; + virtual std::string GetDescription() const; + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_ProgressHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonGUISliderControl +{ +public: + CAddonGUISliderControl(void *hdl, void *cb, CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUISliderControl(void) {} + + virtual void SetVisible(bool yesNo); + virtual std::string GetDescription() const; + + virtual void SetIntRange(int iStart, int iEnd); + virtual void SetIntValue(int iValue); + virtual int GetIntValue() const; + virtual void SetIntInterval(int iInterval); + + virtual void SetPercentage(float fPercent); + virtual float GetPercentage() const; + + virtual void SetFloatRange(float fStart, float fEnd); + virtual void SetFloatValue(float fValue); + virtual float GetFloatValue() const; + virtual void SetFloatInterval(float fInterval); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_SliderHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonGUISettingsSliderControl +{ +public: + CAddonGUISettingsSliderControl(void *hdl, void *cb, CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUISettingsSliderControl(void) {} + + virtual void SetVisible(bool yesNo); + virtual void SetText(const char *label); + virtual std::string GetDescription() const; + + virtual void SetIntRange(int iStart, int iEnd); + virtual void SetIntValue(int iValue); + virtual int GetIntValue() const; + virtual void SetIntInterval(int iInterval); + + virtual void SetPercentage(float fPercent); + virtual float GetPercentage() const; + + virtual void SetFloatRange(float fStart, float fEnd); + virtual void SetFloatValue(float fValue); + virtual float GetFloatValue() const; + virtual void SetFloatInterval(float fInterval); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_SettingsSliderHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonListItem +{ +friend class CAddonGUIWindow; + +public: + CAddonListItem(void *hdl, void *cb, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + virtual ~CAddonListItem(void) {} + + virtual const char *GetLabel(); + virtual void SetLabel(const char *label); + virtual const char *GetLabel2(); + virtual void SetLabel2(const char *label); + virtual void SetIconImage(const char *image); + virtual void SetThumbnailImage(const char *image); + virtual void SetInfo(const char *Info); + virtual void SetProperty(const char *key, const char *value); + virtual const char *GetProperty(const char *key) const; + virtual void SetPath(const char *Path); + +// {(char*)"select(); +// {(char*)"isSelected(); +protected: + GUIHANDLE m_ListItemHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonGUIWindow +{ +friend class CAddonGUISpinControl; +friend class CAddonGUIRadioButton; +friend class CAddonGUIProgressControl; +friend class CAddonGUIRenderingControl; +friend class CAddonGUISliderControl; +friend class CAddonGUISettingsSliderControl; + +public: + CAddonGUIWindow(void *hdl, void *cb, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + virtual ~CAddonGUIWindow(); + + virtual bool Show(); + virtual void Close(); + virtual void DoModal(); + virtual bool SetFocusId(int iControlId); + virtual int GetFocusId(); + virtual bool SetCoordinateResolution(int res); + virtual void SetProperty(const char *key, const char *value); + virtual void SetPropertyInt(const char *key, int value); + virtual void SetPropertyBool(const char *key, bool value); + virtual void SetPropertyDouble(const char *key, double value); + virtual const char *GetProperty(const char *key) const; + virtual int GetPropertyInt(const char *key) const; + virtual bool GetPropertyBool(const char *key) const; + virtual double GetPropertyDouble(const char *key) const; + virtual void ClearProperties(); + virtual int GetListSize(); + virtual void ClearList(); + virtual GUIHANDLE AddStringItem(const char *name, int itemPosition = -1); + virtual void AddItem(GUIHANDLE item, int itemPosition = -1); + virtual void AddItem(CAddonListItem *item, int itemPosition = -1); + virtual void RemoveItem(int itemPosition); + virtual GUIHANDLE GetListItem(int listPos); + virtual void SetCurrentListPosition(int listPos); + virtual int GetCurrentListPosition(); + virtual void SetControlLabel(int controlId, const char *label); + virtual void MarkDirtyRegion(); + + virtual bool OnClick(int controlId); + virtual bool OnFocus(int controlId); + virtual bool OnInit(); + virtual bool OnAction(int actionId); + + GUIHANDLE m_cbhdl; + bool (*CBOnInit)(GUIHANDLE cbhdl); + bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnAction)(GUIHANDLE cbhdl, int actionId); + +protected: + GUIHANDLE m_WindowHandle; + void *m_Handle; + void *m_cb; +}; + +class CAddonGUIRenderingControl +{ +public: + CAddonGUIRenderingControl(void *hdl, void *cb, CAddonGUIWindow *window, int controlId); + virtual ~CAddonGUIRenderingControl(); + virtual void Init(); + + virtual bool Create(int x, int y, int w, int h, void *device); + virtual void Render(); + virtual void Stop(); + virtual bool Dirty(); + + GUIHANDLE m_cbhdl; + bool (*CBCreate)(GUIHANDLE cbhdl, int x, int y, int w, int h, void *device); + void (*CBRender)(GUIHANDLE cbhdl); + void (*CBStop)(GUIHANDLE cbhdl); + bool (*CBDirty)(GUIHANDLE cbhdl); + +private: + CAddonGUIWindow *m_Window; + int m_ControlId; + GUIHANDLE m_RenderingHandle; + void *m_Handle; + void *m_cb; +}; diff --git a/addons/library.xbmc.pvr/libXBMC_pvr.h b/addons/library.xbmc.pvr/libXBMC_pvr.h new file mode 100644 index 0000000..3116514 --- /dev/null +++ b/addons/library.xbmc.pvr/libXBMC_pvr.h @@ -0,0 +1,332 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include +#include +#include +#include "xbmc_pvr_types.h" +#include "libXBMC_addon.h" + +#ifdef _WIN32 +#define PVR_HELPER_DLL "\\library.xbmc.pvr\\libXBMC_pvr" ADDON_HELPER_EXT +#else +#define PVR_HELPER_DLL_NAME "libXBMC_pvr-" ADDON_HELPER_ARCH ADDON_HELPER_EXT +#define PVR_HELPER_DLL "/library.xbmc.pvr/" PVR_HELPER_DLL_NAME +#endif + +#define DVD_TIME_BASE 1000000 +#define DVD_NOPTS_VALUE (-1LL<<52) // should be possible to represent in both double and __int64 + +class CHelper_libXBMC_pvr +{ +public: + CHelper_libXBMC_pvr(void) + { + m_libXBMC_pvr = NULL; + m_Handle = NULL; + } + + ~CHelper_libXBMC_pvr(void) + { + if (m_libXBMC_pvr) + { + PVR_unregister_me(m_Handle, m_Callbacks); + dlclose(m_libXBMC_pvr); + } + } + + /*! + * @brief Resolve all callback methods + * @param handle Pointer to the add-on + * @return True when all methods were resolved, false otherwise. + */ + bool RegisterMe(void* handle) + { + m_Handle = handle; + + std::string libBasePath; + libBasePath = ((cb_array*)m_Handle)->libPath; + libBasePath += PVR_HELPER_DLL; + +#if defined(ANDROID) + struct stat st; + if(stat(libBasePath.c_str(),&st) != 0) + { + std::string tempbin = getenv("XBMC_ANDROID_LIBS"); + libBasePath = tempbin + "/" + PVR_HELPER_DLL_NAME; + } +#endif + + m_libXBMC_pvr = dlopen(libBasePath.c_str(), RTLD_LAZY); + if (m_libXBMC_pvr == NULL) + { + fprintf(stderr, "Unable to load %s\n", dlerror()); + return false; + } + + PVR_register_me = (void* (*)(void *HANDLE)) + dlsym(m_libXBMC_pvr, "PVR_register_me"); + if (PVR_register_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_unregister_me = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_pvr, "PVR_unregister_me"); + if (PVR_unregister_me == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_transfer_epg_entry = (void (*)(void* HANDLE, void* CB, const ADDON_HANDLE handle, const EPG_TAG *epgentry)) + dlsym(m_libXBMC_pvr, "PVR_transfer_epg_entry"); + if (PVR_transfer_epg_entry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_transfer_channel_entry = (void (*)(void* HANDLE, void* CB, const ADDON_HANDLE handle, const PVR_CHANNEL *chan)) + dlsym(m_libXBMC_pvr, "PVR_transfer_channel_entry"); + if (PVR_transfer_channel_entry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_transfer_timer_entry = (void (*)(void* HANDLE, void* CB, const ADDON_HANDLE handle, const PVR_TIMER *timer)) + dlsym(m_libXBMC_pvr, "PVR_transfer_timer_entry"); + if (PVR_transfer_timer_entry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_transfer_recording_entry = (void (*)(void* HANDLE, void* CB, const ADDON_HANDLE handle, const PVR_RECORDING *recording)) + dlsym(m_libXBMC_pvr, "PVR_transfer_recording_entry"); + if (PVR_transfer_recording_entry == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_add_menu_hook = (void (*)(void* HANDLE, void* CB, PVR_MENUHOOK *hook)) + dlsym(m_libXBMC_pvr, "PVR_add_menu_hook"); + if (PVR_add_menu_hook == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_recording = (void (*)(void* HANDLE, void* CB, const char *Name, const char *FileName, bool On)) + dlsym(m_libXBMC_pvr, "PVR_recording"); + if (PVR_recording == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_trigger_timer_update = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_pvr, "PVR_trigger_timer_update"); + if (PVR_trigger_timer_update == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_trigger_recording_update = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_pvr, "PVR_trigger_recording_update"); + if (PVR_trigger_recording_update == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_trigger_channel_update = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_pvr, "PVR_trigger_channel_update"); + if (PVR_trigger_channel_update == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_trigger_channel_groups_update = (void (*)(void* HANDLE, void* CB)) + dlsym(m_libXBMC_pvr, "PVR_trigger_channel_groups_update"); + if (PVR_trigger_channel_groups_update == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_trigger_epg_update = (void (*)(void* HANDLE, void* CB, unsigned int iChannelUid)) + dlsym(m_libXBMC_pvr, "PVR_trigger_epg_update"); + if (PVR_trigger_epg_update == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_transfer_channel_group = (void (*)(void* HANDLE, void* CB, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group)) + dlsym(m_libXBMC_pvr, "PVR_transfer_channel_group"); + if (PVR_transfer_channel_group == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_transfer_channel_group_member = (void (*)(void* HANDLE, void* CB, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member)) + dlsym(m_libXBMC_pvr, "PVR_transfer_channel_group_member"); + if (PVR_transfer_channel_group_member == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + +#ifdef USE_DEMUX + PVR_free_demux_packet = (void (*)(void* HANDLE, void* CB, DemuxPacket* pPacket)) + dlsym(m_libXBMC_pvr, "PVR_free_demux_packet"); + if (PVR_free_demux_packet == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } + + PVR_allocate_demux_packet = (DemuxPacket* (*)(void* HANDLE, void* CB, int iDataSize)) + dlsym(m_libXBMC_pvr, "PVR_allocate_demux_packet"); + if (PVR_allocate_demux_packet == NULL) { fprintf(stderr, "Unable to assign function %s\n", dlerror()); return false; } +#endif + + m_Callbacks = PVR_register_me(m_Handle); + return m_Callbacks != NULL; + } + + /*! + * @brief Transfer an EPG tag from the add-on to XBMC + * @param handle The handle parameter that XBMC used when requesting the EPG data + * @param entry The entry to transfer to XBMC + */ + void TransferEpgEntry(const ADDON_HANDLE handle, const EPG_TAG* entry) + { + return PVR_transfer_epg_entry(m_Handle, m_Callbacks, handle, entry); + } + + /*! + * @brief Transfer a channel entry from the add-on to XBMC + * @param handle The handle parameter that XBMC used when requesting the channel list + * @param entry The entry to transfer to XBMC + */ + void TransferChannelEntry(const ADDON_HANDLE handle, const PVR_CHANNEL* entry) + { + return PVR_transfer_channel_entry(m_Handle, m_Callbacks, handle, entry); + } + + /*! + * @brief Transfer a timer entry from the add-on to XBMC + * @param handle The handle parameter that XBMC used when requesting the timers list + * @param entry The entry to transfer to XBMC + */ + void TransferTimerEntry(const ADDON_HANDLE handle, const PVR_TIMER* entry) + { + return PVR_transfer_timer_entry(m_Handle, m_Callbacks, handle, entry); + } + + /*! + * @brief Transfer a recording entry from the add-on to XBMC + * @param handle The handle parameter that XBMC used when requesting the recordings list + * @param entry The entry to transfer to XBMC + */ + void TransferRecordingEntry(const ADDON_HANDLE handle, const PVR_RECORDING* entry) + { + return PVR_transfer_recording_entry(m_Handle, m_Callbacks, handle, entry); + } + + /*! + * @brief Transfer a channel group from the add-on to XBMC. The group will be created if it doesn't exist. + * @param handle The handle parameter that XBMC used when requesting the channel groups list + * @param entry The entry to transfer to XBMC + */ + void TransferChannelGroup(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP* entry) + { + return PVR_transfer_channel_group(m_Handle, m_Callbacks, handle, entry); + } + + /*! + * @brief Transfer a channel group member entry from the add-on to XBMC. The channel will be added to the group if the group can be found. + * @param handle The handle parameter that XBMC used when requesting the channel group members list + * @param entry The entry to transfer to XBMC + */ + void TransferChannelGroupMember(const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER* entry) + { + return PVR_transfer_channel_group_member(m_Handle, m_Callbacks, handle, entry); + } + + /*! + * @brief Add or replace a menu hook for the context menu for this add-on + * @param hook The hook to add + */ + void AddMenuHook(PVR_MENUHOOK* hook) + { + return PVR_add_menu_hook(m_Handle, m_Callbacks, hook); + } + + /*! + * @brief Display a notification in XBMC that a recording started or stopped on the server + * @param strRecordingName The name of the recording to display + * @param strFileName The filename of the recording + * @param bOn True when recording started, false when it stopped + */ + void Recording(const char* strRecordingName, const char* strFileName, bool bOn) + { + return PVR_recording(m_Handle, m_Callbacks, strRecordingName, strFileName, bOn); + } + + /*! + * @brief Request XBMC to update it's list of timers + */ + void TriggerTimerUpdate(void) + { + return PVR_trigger_timer_update(m_Handle, m_Callbacks); + } + + /*! + * @brief Request XBMC to update it's list of recordings + */ + void TriggerRecordingUpdate(void) + { + return PVR_trigger_recording_update(m_Handle, m_Callbacks); + } + + /*! + * @brief Request XBMC to update it's list of channels + */ + void TriggerChannelUpdate(void) + { + return PVR_trigger_channel_update(m_Handle, m_Callbacks); + } + + /*! + * @brief Schedule an EPG update for the given channel channel + * @param iChannelUid The unique id of the channel for this add-on + */ + void TriggerEpgUpdate(unsigned int iChannelUid) + { + return PVR_trigger_epg_update(m_Handle, m_Callbacks, iChannelUid); + } + + /*! + * @brief Request XBMC to update it's list of channel groups + */ + void TriggerChannelGroupsUpdate(void) + { + return PVR_trigger_channel_groups_update(m_Handle, m_Callbacks); + } + +#ifdef USE_DEMUX + /*! + * @brief Free a packet that was allocated with AllocateDemuxPacket + * @param pPacket The packet to free + */ + void FreeDemuxPacket(DemuxPacket* pPacket) + { + return PVR_free_demux_packet(m_Handle, m_Callbacks, pPacket); + } + + /*! + * @brief Allocate a demux packet. Free with FreeDemuxPacket + * @param iDataSize The size of the data that will go into the packet + * @return The allocated packet + */ + DemuxPacket* AllocateDemuxPacket(int iDataSize) + { + return PVR_allocate_demux_packet(m_Handle, m_Callbacks, iDataSize); + } +#endif + +protected: + void* (*PVR_register_me)(void*); + void (*PVR_unregister_me)(void*, void*); + void (*PVR_transfer_epg_entry)(void*, void*, const ADDON_HANDLE, const EPG_TAG*); + void (*PVR_transfer_channel_entry)(void*, void*, const ADDON_HANDLE, const PVR_CHANNEL*); + void (*PVR_transfer_timer_entry)(void*, void*, const ADDON_HANDLE, const PVR_TIMER*); + void (*PVR_transfer_recording_entry)(void*, void*, const ADDON_HANDLE, const PVR_RECORDING*); + void (*PVR_add_menu_hook)(void*, void*, PVR_MENUHOOK*); + void (*PVR_recording)(void*, void*, const char*, const char*, bool); + void (*PVR_trigger_channel_update)(void*, void*); + void (*PVR_trigger_channel_groups_update)(void*, void*); + void (*PVR_trigger_timer_update)(void*, void*); + void (*PVR_trigger_recording_update)(void* , void*); + void (*PVR_trigger_epg_update)(void*, void*, unsigned int); + void (*PVR_transfer_channel_group)(void*, void*, const ADDON_HANDLE, const PVR_CHANNEL_GROUP*); + void (*PVR_transfer_channel_group_member)(void*, void*, const ADDON_HANDLE, const PVR_CHANNEL_GROUP_MEMBER*); +#ifdef USE_DEMUX + void (*PVR_free_demux_packet)(void*, void*, DemuxPacket*); + DemuxPacket* (*PVR_allocate_demux_packet)(void*, void*, int); +#endif + +private: + void* m_libXBMC_pvr; + void* m_Handle; + void* m_Callbacks; + struct cb_array + { + const char* libPath; + }; +}; diff --git a/project/cmake/addons/CMakeLists.txt b/project/cmake/addons/CMakeLists.txt new file mode 100644 index 0000000..0afc622 --- /dev/null +++ b/project/cmake/addons/CMakeLists.txt @@ -0,0 +1,249 @@ +project(kodi-addons) + +cmake_minimum_required(VERSION 2.8) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if(NOT CORE_SYSTEM_NAME) + string(TOLOWER ${CMAKE_SYSTEM_NAME} CORE_SYSTEM_NAME) +endif() + +include(ExternalProject) + +### setup all the necessary paths +if(NOT APP_ROOT AND NOT XBMCROOT) + set(APP_ROOT ${PROJECT_SOURCE_DIR}/../../..) +elseif(NOT APP_ROOT) + file(TO_CMAKE_PATH "${XBMCROOT}" APP_ROOT) +else() + file(TO_CMAKE_PATH "${APP_ROOT}" APP_ROOT) +endif() +get_filename_component(APP_ROOT "${APP_ROOT}" ABSOLUTE) + +if(NOT BUILD_DIR) + set(BUILD_DIR "${CMAKE_BINARY_DIR}/build") +else() + file(TO_CMAKE_PATH "${BUILD_DIR}" BUILD_DIR) +endif() +get_filename_component(BUILD_DIR "${BUILD_DIR}" ABSOLUTE) + +if(NOT DEPENDS_PATH) + set(DEPENDS_PATH "${BUILD_DIR}/depends") +else() + file(TO_CMAKE_PATH "${DEPENDS_PATH}" DEPENDS_PATH) +endif() +get_filename_component(DEPENDS_PATH "${DEPENDS_PATH}" ABSOLUTE) + +if(NOT PLATFORM_DIR) + set(PLATFORM_DIR ${APP_ROOT}/project/cmake/platform/${CORE_SYSTEM_NAME}) + file(TO_CMAKE_PATH "${PLATFORM_DIR}" PLATFORM_DIR) +endif() + +# make sure CMAKE_PREFIX_PATH is set +if(NOT CMAKE_PREFIX_PATH) + set(CMAKE_PREFIX_PATH "${DEPENDS_PATH}") +else() + file(TO_CMAKE_PATH "${CMAKE_PREFIX_PATH}" CMAKE_PREFIX_PATH) + list(APPEND CMAKE_PREFIX_PATH "${DEPENDS_PATH}") +endif() + +# check for autoconf stuff to pass on +if(AUTOCONF_FILES) + separate_arguments(AUTOCONF_FILES) + set(CROSS_AUTOCONF "yes") +endif() + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR NOT CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/output/addons") +endif() +list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}) + +set(BUILD_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} + -DCMAKE_INSTALL_PREFIX:PATH= + -DPACKAGE_CONFIG_PATH=${DEPENDS_PATH}/lib/pkgconfig + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_USER_MAKE_RULES_OVERRIDE=${CMAKE_USER_MAKE_RULES_OVERRIDE} + -DCMAKE_USER_MAKE_RULES_OVERRIDE_CXX=${CMAKE_USER_MAKE_RULES_OVERRIDE_CXX} + -DCORE_SYSTEM_NAME=${CORE_SYSTEM_NAME} + -DBUILD_SHARED_LIBS=1 + -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}) + +if(PACKAGE_ZIP) + # needed for project installing + list(APPEND BUILD_ARGS -DPACKAGE_ZIP=1) + MESSAGE("package zip specified") +endif() + +if(CMAKE_TOOLCHAIN_FILE) + list(APPEND BUILD_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) + MESSAGE("toolchain specified") + MESSAGE(${BUILD_ARGS}) +endif() + +if(NOT ADDONS_TO_BUILD) + set(ADDONS_TO_BUILD "all") +else() + message(STATUS "Building following addons: ${ADDONS_TO_BUILD}") + separate_arguments(ADDONS_TO_BUILD) +endif() + +if(NOT KODI_LIB_DIR) + set(KODI_LIB_DIR "${DEPENDS_PATH}/lib/kodi") +else() + file(TO_CMAKE_PATH "${KODI_LIB_DIR}" KODI_LIB_DIR) +endif() + +# check for platform specific stuff +if(EXISTS ${PLATFORM_DIR}/defines.txt) + file(STRINGS ${PLATFORM_DIR}/defines.txt platformdefines) + + if(NOT ARCH_DEFINES AND platformdefines) + set(ARCH_DEFINES ${platformdefines}) + endif() +endif() + +# include check_target_platform() function +include(${APP_ROOT}/project/cmake/scripts/common/check_target_platform.cmake) + +# check install permissions +set(ADDON_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}) +check_install_permissions(${CMAKE_INSTALL_PREFIX} can_write) +if(NOT ${can_write} AND NOT WIN32) + set(NEED_SUDO TRUE) + set(ADDON_INSTALL_DIR ${CMAKE_BINARY_DIR}/.install) + message(STATUS "NEED_SUDO: ${NEED_SUDO}") +endif() + +### prepare the build environment for the binary addons +# copy the prepare-env.cmake script to the depends path so that we can include it +file(COPY ${APP_ROOT}/project/cmake/scripts/common/prepare-env.cmake DESTINATION ${KODI_LIB_DIR}) + +# add the location of prepare-env.cmake to CMAKE_MODULE_PATH so that it is found +list(APPEND CMAKE_MODULE_PATH ${KODI_LIB_DIR}) + +# include prepare-env.cmake which contains the logic to install the addon header bindings etc +include(prepare-env) + +### add the depends subdirectory for any general dependencies +add_subdirectory(depends) + +### get and build all the binary addons +# look for all the addons to be built +file(GLOB_RECURSE addons ${PROJECT_SOURCE_DIR}/addons/*.txt) +foreach(addon ${addons}) + if(NOT (addon MATCHES platforms.txt)) + file(STRINGS ${addon} def) + separate_arguments(def) + list(GET def 0 id) + + list(FIND ADDONS_TO_BUILD ${id} idx) + if(idx GREATER -1 OR ADDONS_TO_BUILD STREQUAL "all") + get_filename_component(dir ${addon} PATH) + + # check if the addon has a platforms.txt + set(platform_found FALSE) + check_target_platform(${dir} ${CORE_SYSTEM_NAME} platform_found) + + if (${platform_found}) + # make sure the output directory is clean + if(EXISTS "${CMAKE_INSTALL_PREFIX}/${id}") + file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}/${id}/") + endif() + + # get the URL and revision of the addon + list(LENGTH def deflength) + list(GET def 1 url) + + set(archive_name ${id}) + + # if there is a 3rd parameter in the file, we consider it a git revision + if(deflength GREATER 2) + list(GET def 2 revision) + + # Note: downloading specific revisions via http in the format below is probably github specific + # if we ever use other repositories, this might need adapting + set(url ${url}/archive/${revision}.tar.gz) + set(archive_name ${archive_name}-${revision}) + endif() + + # download and extract the addon + if(NOT EXISTS ${BUILD_DIR}/download/${archive_name}.tar.gz) + # cleanup any of the previously downloaded archives of this addon + file(GLOB archives "${BUILD_DIR}/download/${id}*.tar.gz") + if(archives) + message(STATUS "Removing old archives of ${id}: ${archives}") + file(REMOVE ${archives}) + endif() + + # download the addon + file(DOWNLOAD "${url}" "${BUILD_DIR}/download/${archive_name}.tar.gz" STATUS dlstatus LOG dllog SHOW_PROGRESS) + list(GET dlstatus 0 retcode) + if(NOT ${retcode} EQUAL 0) + message(FATAL_ERROR "ERROR downloading ${url} - status: ${dlstatus} log: ${dllog}") + endif() + endif() + + # remove any previously extracted version of the addon + if(EXISTS "${BUILD_DIR}/${id}") + file(REMOVE_RECURSE "${BUILD_DIR}/${id}") + endif() + + # extract the addon from the archive + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzvf ${BUILD_DIR}/download/${archive_name}.tar.gz + WORKING_DIRECTORY ${BUILD_DIR}) + file(GLOB extract_dir "${BUILD_DIR}/${archive_name}*") + if(extract_dir STREQUAL "") + message(FATAL_ERROR "Error extracting ${BUILD_DIR}/download/${archive_name}.tar.gz") + else() + file(RENAME "${extract_dir}" "${BUILD_DIR}/${id}") + endif() + + list(APPEND downloaded_addons ${id}) + + endif() + endif() + endif() +endforeach() + +foreach(id ${downloaded_addons}) + externalproject_add(${id} + SOURCE_DIR ${BUILD_DIR}/${id} + INSTALL_DIR ${ADDON_INSTALL_DIR} + CMAKE_ARGS ${BUILD_ARGS}) + + # add a custom step to the external project between the configure and the build step which will always + # be executed and therefore forces a re-build of all changed files + externalproject_add_step(${id} forcebuild + COMMAND ${CMAKE_COMMAND} -E echo "Force build of ${id}" + DEPENDEES configure + DEPENDERS build + ALWAYS 1) + + # add "kodi-platform" as a dependency to every addon + add_dependencies(${id} kodi-platform) + + set(${id}_DEPENDS_DIR ${BUILD_DIR}/${id}/depends) + + if(EXISTS ${${id}_DEPENDS_DIR}) + include(${APP_ROOT}/project/cmake/scripts/common/handle-depends.cmake) + add_addon_depends(${id} ${${id}_DEPENDS_DIR}) + if (${id}_DEPS AND NOT "${${id}_DEPS}" STREQUAL "") + message(STATUS "${id} DEPENDENCIES: ${${id}_DEPS}") + add_dependencies(${id} ${${id}_DEPS}) + endif() + endif() +endforeach() + +if(NEED_SUDO) + add_custom_target(install + COMMAND ${CMAKE_COMMAND} -E echo "\n\n" + COMMAND ${CMAKE_COMMAND} -E echo "WARNING: sudo rights needed to install to ${CMAKE_INSTALL_PREFIX}\n" + COMMAND sudo ${CMAKE_COMMAND} -E copy_directory ${ADDON_INSTALL_DIR}/ ${CMAKE_INSTALL_PREFIX}/ + COMMAND sudo ${CMAKE_COMMAND} -E remove_directory ${ADDON_INSTALL_DIR}/ + COMMAND sudo -k) +endif() diff --git a/project/cmake/addons/README b/project/cmake/addons/README new file mode 100644 index 0000000..c66e668 --- /dev/null +++ b/project/cmake/addons/README @@ -0,0 +1,68 @@ +KODI ADDONS +=========== +This directory contains the cmake-based buildsystem for addons. It looks into +the "addons" sub-directory and parses all *.txt files recursively. Each addon +must have its own .txt file in a separate sub-directory which must +follow the defined format: + +where + * must be identical to the addon's ID as defined in the addon's + addon.xml + * must be the URL of the git repository containing the addon. + * must be a valid git tag/branch/commit in the addon's git + repository which will be used for the build. + +Reserved filenames (for additional information on how to build an addon) +are: + * platforms.txt: List of platforms to build an addon for (or "all"). It is + also supported to specify negated platforms with a leading exclamation mark + (i), e.g. "!windows". + Available platforms are: linux, windows, darwin, ios, android, rbpi + +The buildsystem uses the following variables (which can be passed into it when +executing cmake with the -D= option) to e.g. access +specific paths: + * ADDONS_TO_BUILD is a quoted, space delimited list of s that + you want to build (default is "all"). + * CMAKE_BUILD_TYPE specifies the type of the build. This can be either "Debug" + or "Release" (default is "Release"). + * CMAKE_INSTALL_PREFIX points to the directory where the built addons and their + additional files (addon.xml, resources ...) will be installed to (defaults + to ). + * CMAKE_TOOLCHAIN_FILE can be used to pass a toolchain file into the add-on + builds. + * DEPENDS_PATH points to the directory containing the "include" and "lib" + directories of the addons' dependencies. + * APP_ROOT points to the root directory of the project (default is the + absolute representation of ../../.. starting from this directory). + * BUILD_DIR points to the directory where the addons and their dependencies + will be downloaded and built. + * PACKAGE_ZIP=1 will mean the add-ons will be 'packaged' into a common folder, + rather than being placed in /lib/kodi/addons and + /share/kodi/addons. + * ARCH_DEFINES specifies the platform-specific C/C++ preprocessor defines + (defaults to empty). + +The buildsystem makes some assumptions about the environment which must be met +by whoever uses it: + * Any dependencies of the addons must already be built and their include and + library files must be present in the path pointed to by (in + "include" and "lib" sub-directories). + +To trigger the cmake-based buildsystem the following command must be executed +with being the path to this directory (absolute or relative, allowing for +in-source and out-of-source builds). + + cmake -G + +cmake supports multiple generators, see +http://www.cmake.org/cmake/help/v2.8.8/cmake.html#section_Generators for a list. + +In case of additional options the call might look like this + + cmake [-G ] \ + -DCMAKE_BUILD_TYPE=Release \ + -DAPP_ROOT="" \ + -DARCH_DEFINES="-DTARGET_LINUX" \ + -DDEPENDS_PATH="" \ + -DCMAKE_INSTALL_PREFIX="/cmake" sub-directories and parses +all *.txt files recursively. Each dependency must have its own .txt +file (either in the main sub-directory or in a separate subdirectory of the main +subdirectory) which must follow one of the defined formats: + * an empty file means that no extra downloads are necessary + * + * + * +where + * must be identical to the filename + * must be the URL to an archive that is downloaded and extracted. + * must be the URL of the git repository containing the + dependency. + * must be a valid git tag/branch/commit in the dependency's git + repository which will be used for the build. + +Reserved filenames (for additional information on how to build a dependency) +are: + * CMakeLists.txt: build instructions for the dependency + * install.txt: instructions on how to install the dependency's built files + * noinstall.txt: no installation step required (content is ignored) + * flags.txt: additional build flags + * deps.txt: whitespace separated list of dependencies of this dependency + +The buildsystem uses the following variables (which can be passed into it when +executing cmake with the -D= option) to e.g. access +specific paths: + * CMAKE_BUILD_TYPE specifies the type of the build. This can be either "Debug" + or "Release" (default is "Release"). + * CMAKE_TOOLCHAIN_FILE can be used to pass a toolchain file into the add-on + builds. + * CORE_SYSTEM_NAME is the name of the platform (e.g. "linux" or "android") in + lower-case (defaults to lowercase(CMAKE_SYSTEM_NAME)). + * APP_ROOT points to the root directory of the project (default is the + absolute representation of ../../.. starting from this directory). + * DEPENDS_PATH points to the directory where the built dependencies + (their include and library file) will be installed to. + * ARCH_DEFINES specifies the platform-specific C/C++ preprocessor defines + (defaults to empty). + * DEPENDS_TO_BUILD is a quoted, space delimited list of s that + you want to build (default is "all"). + +To trigger the cmake-based buildsystem the following command must be executed +with being the path to this directory (absolute or relative, allowing for +in-source and out-of-source builds). + + cmake -G + +cmake supports multiple generators, see +http://www.cmake.org/cmake/help/v2.8.8/cmake.html#section_Generators for a list. + +In case of additional options the call might look like this + + cmake [-G ] \ + -DCMAKE_BUILD_TYPE=Release \ + -DAPP_ROOT="" \ + -DARCH_DEFINES="-DTARGET_LINUX" \ + -DCMAKE_INSTALL_PREFIX=" being the path to this directory (absolute or relative, allowing for +in-source and out-of-source builds). + + cmake [-G ] + +cmake supports multiple generators, see +http://www.cmake.org/cmake/help/v2.8.8/cmake.html#section_Generators for a list. +For win32 builds one of the "Visual Studio XX" or the "NMake Makefiles" +generators is preferred. For the "NMake Makefiles" generator to work the above +command must be called from an environment prepared for VC++ builds (see +http://msdn.microsoft.com/en-us/library/f2ccy3wt.aspx). diff --git a/project/cmake/addons/depends/windows/extract-7z.cmake b/project/cmake/addons/depends/windows/extract-7z.cmake new file mode 100644 index 0000000..95a2672 --- /dev/null +++ b/project/cmake/addons/depends/windows/extract-7z.cmake @@ -0,0 +1,10 @@ +get_filename_component(file ${URL} NAME) +file(DOWNLOAD ${URL} ${DEST}/${file}) +execute_process(COMMAND ${7ZIP_EXECUTABLE} -y x ${DEST}/${file} + WORKING_DIRECTORY ${DESTDIR}) +if(${file} MATCHES .tar) + string(REPLACE ".7z" "" tarball ${file}) + string(REPLACE ".lzma" "" tarball ${file}) + execute_process(COMMAND ${7ZIP_EXECUTABLE} -y x ${DESTDIR}/${tarball} + WORKING_DIRECTORY ${DESTDIR}) +endif() diff --git a/project/cmake/addons/depends/windows/extract-direct.cmake b/project/cmake/addons/depends/windows/extract-direct.cmake new file mode 100644 index 0000000..13cb74f --- /dev/null +++ b/project/cmake/addons/depends/windows/extract-direct.cmake @@ -0,0 +1,2 @@ +get_filename_component(file ${URL} NAME) +file(DOWNLOAD ${URL} ${DEST}/${file}) diff --git a/project/cmake/addons/depends/windows/install.cmake b/project/cmake/addons/depends/windows/install.cmake new file mode 100644 index 0000000..9a3adbb --- /dev/null +++ b/project/cmake/addons/depends/windows/install.cmake @@ -0,0 +1,24 @@ +if(EXISTS "${INPUTFILE}") + # if there's an input file we use it to determine which files to copy where + file(STRINGS ${INPUTFILE} FILES) + string(REPLACE "\n" ";" FILES "${FILES}") + foreach(file ${FILES}) + string(REPLACE " " ";" file "${file}") + list(GET file 0 dir) + list(GET file 1 dest) + list(LENGTH file deflength) + if(deflength GREATER 2) + list(GET file 2 copy) + endif() + file(GLOB files ${INPUTDIR}/${dir}) + foreach(instfile ${files}) + file(COPY ${instfile} DESTINATION ${DESTDIR}/${dest}) + if(copy) + file(COPY ${instfile} DESTINATION ${DESTDIR}/${copy}) + endif() + endforeach() + endforeach() +else() + # otherwise we assume that the content of the extracted archive is already well-formed and can just be copied + file(COPY ${INPUTDIR}/${dir} DESTINATION ${DESTDIR}) +endif() \ No newline at end of file diff --git a/project/cmake/addons/depends/windows/prebuilt/README b/project/cmake/addons/depends/windows/prebuilt/README new file mode 100644 index 0000000..a0c70d6 --- /dev/null +++ b/project/cmake/addons/depends/windows/prebuilt/README @@ -0,0 +1,21 @@ +KODI WIN32 PREBUILT ADDON DEPENDENCIES +====================================== +This directory contains a file or sub-directory for every prebuilt dependency +used by one of the addons being built. There are two different modes supported. +Both include a file named .txt which must follow the defined format + + +If the archive, which the points at, contains + * only the necessary files and in the proper directory structure (i.e. an + "include" and a "lib" directory) then the file must be put into this + directory and nothing else is needed. + * unnecessary files and/or does not follow the defined directory structure + (i.e. an "include" and a "lib" directory) then the file must be put into a + sub-directory named . Furthermore an additional file called + "install.txt" must be placed in that sub-directory. install.txt contains a + line for every path/directory/file with a destination where it must be copied + to. It must follow the defined format + [] + where must be an existing file, directory or a path containing + wildcards, and the optional must be existing + directories. diff --git a/project/cmake/kodi-config.cmake.in b/project/cmake/kodi-config.cmake.in new file mode 100644 index 0000000..0e3b158 --- /dev/null +++ b/project/cmake/kodi-config.cmake.in @@ -0,0 +1,11 @@ +SET(KODI_INCLUDE_DIR @prefix@/include) +SET(APP_NAME @APP_NAME@) +SET(APP_VERSION_MAJOR @APP_VERSION_MAJOR@) +SET(APP_VERSION_MINOR @APP_VERSION_MINOR@) +IF(NOT WIN32) + SET(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} @CXX11_SWITCH@") +ENDIF() +LIST(APPEND CMAKE_MODULE_PATH @prefix@/lib/kodi) +ADD_DEFINITIONS(@ARCH_DEFINES@) + +include(addon-helpers) diff --git a/project/cmake/platform/android/defines.txt b/project/cmake/platform/android/defines.txt new file mode 100644 index 0000000..fa103d0 --- /dev/null +++ b/project/cmake/platform/android/defines.txt @@ -0,0 +1 @@ +-DTARGET_POSIX -DTARGET_LINUX -D_LINUX -DTARGET_ANDROID diff --git a/project/cmake/platform/darwin/defines.txt b/project/cmake/platform/darwin/defines.txt new file mode 100644 index 0000000..33b009e --- /dev/null +++ b/project/cmake/platform/darwin/defines.txt @@ -0,0 +1 @@ +-DTARGET_POSIX -DTARGET_DARWIN -DTARGET_DARWIN_OSX -D_LINUX diff --git a/project/cmake/platform/freebsd/defines.txt b/project/cmake/platform/freebsd/defines.txt new file mode 100644 index 0000000..9483597 --- /dev/null +++ b/project/cmake/platform/freebsd/defines.txt @@ -0,0 +1 @@ +-DTARGET_POSIX -DTARGET_FREEBSD -D_LINUX diff --git a/project/cmake/platform/ios/defines.txt b/project/cmake/platform/ios/defines.txt new file mode 100644 index 0000000..d0989ea --- /dev/null +++ b/project/cmake/platform/ios/defines.txt @@ -0,0 +1 @@ +-DTARGET_POSIX -DTARGET_DARWIN -DTARGET_DARWIN_IOS -D_LINUX diff --git a/project/cmake/platform/linux/defines.txt b/project/cmake/platform/linux/defines.txt new file mode 100644 index 0000000..3fe9c5c --- /dev/null +++ b/project/cmake/platform/linux/defines.txt @@ -0,0 +1 @@ +-DTARGET_POSIX -DTARGET_LINUX -D_LINUX -fPIC diff --git a/project/cmake/platform/rbpi/defines.txt b/project/cmake/platform/rbpi/defines.txt new file mode 100644 index 0000000..08fe4cb --- /dev/null +++ b/project/cmake/platform/rbpi/defines.txt @@ -0,0 +1 @@ +-DTARGET_POSIX -DTARGET_LINUX -D_LINUX -D_ARMEL -DTARGET_RASPBERRY_PI diff --git a/project/cmake/platform/windows/defines.txt b/project/cmake/platform/windows/defines.txt new file mode 100644 index 0000000..5ccd98a --- /dev/null +++ b/project/cmake/platform/windows/defines.txt @@ -0,0 +1 @@ +-DTARGET_WINDOWS -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -D_USE_32BIT_TIME_T -D_WINSOCKAPI_ \ No newline at end of file diff --git a/project/cmake/scripts/common/addon-helpers.cmake b/project/cmake/scripts/common/addon-helpers.cmake new file mode 100644 index 0000000..b94df2a --- /dev/null +++ b/project/cmake/scripts/common/addon-helpers.cmake @@ -0,0 +1,113 @@ +# Workaround for the fact that cpack's filenames are not customizable. +# Each add-on is added as a separate component to facilitate zip/tgz packaging. +# The filenames are always of the form basename-component, which is +# incompatible with the addonid-version scheme we want. This hack renames +# the files from the file names generated by the 'package' target. +# Sadly we cannot extend the 'package' target, as it is a builtin target, see +# http://public.kitware.com/Bug/view.php?id=8438 +# Thus, we have to add an 'addon-package' target. +add_custom_target(addon-package + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target package) + +macro(add_cpack_workaround target version ext) + add_custom_command(TARGET addon-package PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E rename addon-${target}-${version}.${ext} ${target}-${version}.${ext}) +endmacro() + +# Grab the version from a given add-on's addon.xml +macro (addon_version dir prefix) + FILE(READ ${dir}/addon.xml ADDONXML) + STRING(REGEX MATCH "]*version.?=.?.[0-9\\.]+" VERSION_STRING ${ADDONXML}) + STRING(REGEX REPLACE ".*version=.([0-9\\.]+).*" "\\1" ${prefix}_VERSION ${VERSION_STRING}) + message(STATUS ${prefix}_VERSION=${${prefix}_VERSION}) +endmacro() + +# Build, link and optionally package an add-on +macro (build_addon target prefix libs) + ADD_LIBRARY(${target} ${${prefix}_SOURCES}) + TARGET_LINK_LIBRARIES(${target} ${${libs}}) + addon_version(${target} ${prefix}) + SET_TARGET_PROPERTIES(${target} PROPERTIES VERSION ${${prefix}_VERSION} + SOVERSION ${APP_VERSION_MAJOR}.${APP_VERSION_MINOR} + PREFIX "") + IF(OS STREQUAL "android") + SET_TARGET_PROPERTIES(${target} PROPERTIES PREFIX "lib") + ENDIF(OS STREQUAL "android") + + # set zip as default if addon-package is called without PACKAGE_XXX + SET(CPACK_GENERATOR "ZIP") + SET(ext "zip") + IF(PACKAGE_ZIP OR PACKAGE_TGZ) + IF(PACKAGE_TGZ) + SET(CPACK_GENERATOR "TGZ") + SET(ext "tar.gz") + ENDIF(PACKAGE_TGZ) + SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) + set(CPACK_PACKAGE_FILE_NAME addon) + IF(CMAKE_BUILD_TYPE STREQUAL "Release") + SET(CPACK_STRIP_FILES TRUE) + ENDIF(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) + set(CPACK_COMPONENTS_IGNORE_GROUPS 1) + list(APPEND CPACK_COMPONENTS_ALL ${target}-${${prefix}_VERSION}) + # Pack files together to create an archive + INSTALL(DIRECTORY ${target} DESTINATION ./ COMPONENT ${target}-${${prefix}_VERSION}) + IF(WIN32) + # get the installation location for the addon's target + get_property(dll_location TARGET ${target} PROPERTY LOCATION) + # in case of a VC++ project the installation location contains a $(Configuration) VS variable + # we replace it with ${CMAKE_BUILD_TYPE} (which doesn't cover the case when the build configuration + # is changed within Visual Studio) + string(REPLACE "$(Configuration)" "${CMAKE_BUILD_TYPE}" dll_location "${dll_location}") + + # install the generated DLL file + INSTALL(PROGRAMS ${dll_location} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}) + + IF(CMAKE_BUILD_TYPE MATCHES Debug) + # for debug builds also install the PDB file + get_filename_component(dll_directory ${dll_location} DIRECTORY) + INSTALL(FILES ${dll_directory}/${target}.pdb DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}) + ENDIF() + ELSE(WIN32) + INSTALL(TARGETS ${target} DESTINATION ${target} + COMPONENT ${target}-${${prefix}_VERSION}) + ENDIF(WIN32) + add_cpack_workaround(${target} ${${prefix}_VERSION} ${ext}) + ELSE(PACKAGE_ZIP OR PACKAGE_TGZ) + INSTALL(TARGETS ${target} DESTINATION lib/kodi/addons/${target}) + INSTALL(DIRECTORY ${target} DESTINATION share/kodi/addons) + ENDIF(PACKAGE_ZIP OR PACKAGE_TGZ) +endmacro() + +# finds a path to a given file (recursive) +function (kodi_find_path var_name filename search_path strip_file) + file(GLOB_RECURSE PATH_TO_FILE ${search_path} ${filename}) + if(strip_file) + string(REPLACE ${filename} "" PATH_TO_FILE ${PATH_TO_FILE}) + endif(strip_file) + set (${var_name} ${PATH_TO_FILE} PARENT_SCOPE) +endfunction() + +# Cmake build options +include(addoptions) +include(TestCXXAcceptsFlag) +OPTION(PACKAGE_ZIP "Package Zip file?" OFF) +OPTION(PACKAGE_TGZ "Package TGZ file?" OFF) +OPTION(BUILD_SHARED_LIBS "Build shared libs?" ON) + +# LTO support? +CHECK_CXX_ACCEPTS_FLAG("-flto" HAVE_LTO) +IF(HAVE_LTO) + OPTION(USE_LTO "use link time optimization" OFF) + IF(USE_LTO) + add_options(ALL_LANGUAGES ALL_BUILDS "-flto") + ENDIF(USE_LTO) +ENDIF(HAVE_LTO) + +# set this to try linking dependencies as static as possible +IF(ADDONS_PREFER_STATIC_LIBS) + SET(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) +ENDIF(ADDONS_PREFER_STATIC_LIBS) + diff --git a/project/cmake/scripts/common/addoptions.cmake b/project/cmake/scripts/common/addoptions.cmake new file mode 100644 index 0000000..0ebb823 --- /dev/null +++ b/project/cmake/scripts/common/addoptions.cmake @@ -0,0 +1,82 @@ +# - Add options without repeating them on the command line +# +# Synopsis: +# +# add_options (lang build opts) +# +# where: +# +# lang Name of the language whose compiler should receive the +# options, e.g. CXX. If a comma-separated list is received +# then the option is added for all those languages. Use the +# special value ALL_LANGUAGES for these languages: CXX, C +# and Fortran +# +# build Kind of build to which this options should apply, +# such as DEBUG and RELEASE. This can also be a comma- +# separated list. Use the special value ALL_BUILDS to apply +# to all builds. +# +# opts List of options to add. Each should be quoted. +# +# Example: +# +# add_options (CXX RELEASE "-O3" "-DNDEBUG" "-Wall") + +function (add_options langs builds) + # special handling of empty language specification + if ("${langs}" STREQUAL "ALL_LANGUAGES") + set (langs CXX C Fortran) + endif ("${langs}" STREQUAL "ALL_LANGUAGES") + foreach (lang IN LISTS langs) + # prepend underscore if necessary + foreach (build IN LISTS builds) + if (NOT ("${build}" STREQUAL "ALL_BUILDS")) + set (_bld "_${build}") + string (TOUPPER "${_bld}" _bld) + else (NOT ("${build}" STREQUAL "ALL_BUILDS")) + set (_bld "") + endif (NOT ("${build}" STREQUAL "ALL_BUILDS")) + foreach (_opt IN LISTS ARGN) + set (_var "CMAKE_${lang}_FLAGS${_bld}") + #message (STATUS "Adding \"${_opt}\" to \${${_var}}") + # remove it first + string (REPLACE "${_opt}" "" _without "${${_var}}") + string (STRIP "${_without}" _without) + # we need to strip this one as well, so they are comparable + string (STRIP "${${_var}}" _stripped) + # if it wasn't there, then add it at the end + if ("${_without}" STREQUAL "${_stripped}") + # don't add any extra spaces if no options yet are set + if (NOT ${_stripped} STREQUAL "") + set (${_var} "${_stripped} ${_opt}") + else (NOT ${_stripped} STREQUAL "") + set (${_var} "${_opt}") + endif (NOT ${_stripped} STREQUAL "") + set (${_var} "${${_var}}" PARENT_SCOPE) + endif ("${_without}" STREQUAL "${_stripped}") + endforeach (_opt) + endforeach (build) + endforeach (lang) +endfunction (add_options lang build) + +# set varname to flag unless user has specified something that matches regex +function (set_default_option varname flag regex) + if (NOT "$ENV{CXXFLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}" MATCHES "${regex}") + set (${varname} ${flag} PARENT_SCOPE) + else (NOT "$ENV{CXXFLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}" MATCHES "${regex}") + set (${varname} PARENT_SCOPE) + endif (NOT "$ENV{CXXFLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS}" MATCHES "${regex}" + AND NOT "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}}" MATCHES "${regex}") +endfunction (set_default_option) + +# note: this must be called before project() +macro (no_default_options) + # prevent the platform probe to set options + set (CMAKE_NOT_USING_CONFIG_FLAGS TRUE) +endmacro (no_default_options) diff --git a/project/cmake/scripts/common/check_target_platform.cmake b/project/cmake/scripts/common/check_target_platform.cmake new file mode 100644 index 0000000..fc8b403 --- /dev/null +++ b/project/cmake/scripts/common/check_target_platform.cmake @@ -0,0 +1,61 @@ +# handle target platforms +function(check_target_platform dir target_platform build) + # param[in] dir path/directory of the addon/dependency + # param[in] target_platform target platform of the build + # param[out] build Result whether the addon/dependency should be built for the specified target platform + + set(${build} FALSE) + # check if the given directory exists and contains a platforms.txt + if(EXISTS ${dir} AND EXISTS ${dir}/platforms.txt) + # get all the specified platforms + file(STRINGS ${dir}/platforms.txt platforms) + separate_arguments(platforms) + + # check if the addon/dependency should be built for the current platform + foreach(platform ${platforms}) + if(${platform} STREQUAL "all" OR ${platform} STREQUAL ${target_platform}) + set(${build} TRUE) + else() + # check if the platform is defined as "!" + string(SUBSTRING ${platform} 0 1 platform_first) + if(${platform_first} STREQUAL "!") + # extract the platform + string(LENGTH ${platform} platform_length) + MATH(EXPR platform_length "${platform_length} - 1") + string(SUBSTRING ${platform} 1 ${platform_length} platform) + + # check if the current platform does not match the extracted platform + if (NOT ${platform} STREQUAL ${target_platform}) + set(${build} TRUE) + endif() + endif() + endif() + endforeach() + else() + set(${build} TRUE) + endif() + + # make the ${build} variable available to the calling script + set(${build} "${${build}}" PARENT_SCOPE) +endfunction() + +function(check_install_permissions install_dir have_perms) + # param[in] install_dir directory to check for write permissions + # param[out] have_perms wether we have permissions to install to install_dir + + set(${have_perms} TRUE) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}/lib/kodi + COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}/share/kodi + COMMAND ${CMAKE_COMMAND} -E touch ${install_dir}/lib/kodi/.cmake-inst-test ${install_dir}/share/kodi/.cmake-inst-test + RESULT_VARIABLE permtest) + + if(${permtest} GREATER 0) + message(STATUS "check_install_permissions: ${permtest}") + set(${have_perms} FALSE) + endif() + set(${have_perms} "${${have_perms}}" PARENT_SCOPE) + + if(EXISTS ${install_dir}/lib/kodi/.cmake-inst-test OR EXISTS ${install_dir}/share/kodi/.cmake-inst-test) + file(REMOVE ${install_dir}/lib/kodi/.cmake-inst-test ${install_dir}/share/kodi/.cmake-inst-test) + endif() +endfunction() diff --git a/project/cmake/scripts/common/handle-depends.cmake b/project/cmake/scripts/common/handle-depends.cmake new file mode 100644 index 0000000..b3bf3cd --- /dev/null +++ b/project/cmake/scripts/common/handle-depends.cmake @@ -0,0 +1,191 @@ +include(${APP_ROOT}/project/cmake/scripts/common/check_target_platform.cmake) + +# handle addon depends +function(add_addon_depends addon searchpath) + # input: string addon string searchpath + + set(OUTPUT_DIR ${DEPENDS_PATH}) + file(GLOB_RECURSE cmake_input_files ${searchpath}/${CORE_SYSTEM_NAME}/*.txt) + file(GLOB_RECURSE cmake_input_files2 ${searchpath}/common/*.txt) + list(APPEND cmake_input_files ${cmake_input_files2}) + + foreach(file ${cmake_input_files}) + if(NOT (file MATCHES CMakeLists.txt OR + file MATCHES install.txt OR + file MATCHES noinstall.txt OR + file MATCHES flags.txt OR + file MATCHES deps.txt OR + file MATCHES platforms.txt)) + message(STATUS "Processing ${file}") + file(STRINGS ${file} def) + separate_arguments(def) + list(LENGTH def deflength) + get_filename_component(dir ${file} PATH) + + # get the id of the dependency + if(NOT "${def}" STREQUAL "") + # read the id from the file + list(GET def 0 id) + else() + # read the id from the filename + get_filename_component(id ${file} NAME_WE) + endif() + + # check if the dependency has a platforms.txt + set(platform_found FALSE) + check_target_platform(${dir} ${CORE_SYSTEM_NAME} platform_found) + + if(${platform_found} AND NOT TARGET ${id}) + # determine the download URL of the dependency + set(url "") + if(deflength GREATER 1) + list(GET def 1 url) + message(STATUS "${id} url: ${url}") + endif() + + # check if there are any library specific flags that need to be passed on + if(EXISTS ${dir}/flags.txt) + file(STRINGS ${dir}/flags.txt extraflags) + separate_arguments(extraflags) + message(STATUS "${id} extraflags: ${extraflags}") + endif() + + set(BUILD_ARGS -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} + -DOUTPUT_DIR=${OUTPUT_DIR} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_USER_MAKE_RULES_OVERRIDE=${CMAKE_USER_MAKE_RULES_OVERRIDE} + -DCMAKE_USER_MAKE_RULES_OVERRIDE_CXX=${CMAKE_USER_MAKE_RULES_OVERRIDE_CXX} + -DCMAKE_INSTALL_PREFIX=${OUTPUT_DIR} + -DCORE_SYSTEM_NAME=${CORE_SYSTEM_NAME} + -DENABLE_STATIC=1 + -DBUILD_SHARED_LIBS=0) + # if there are no make rules override files available take care of manually passing on ARCH_DEFINES + if(NOT CMAKE_USER_MAKE_RULES_OVERRIDE AND NOT CMAKE_USER_MAKE_RULES_OVERRIDE_CXX) + # make sure we create strings, not lists + set(TMP_C_FLAGS "${CMAKE_C_FLAGS} ${ARCH_DEFINES}") + set(TMP_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ARCH_DEFINES}") + list(APPEND BUILD_ARGS -DCMAKE_C_FLAGS=${TMP_C_FLAGS} + -DCMAKE_CXX_FLAGS=${TMP_CXX_FLAGS}) + endif() + + if(CMAKE_TOOLCHAIN_FILE) + list(APPEND BUILD_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) + MESSAGE("toolchain specified") + MESSAGE(${BUILD_ARGS}) + endif() + + # if there's a CMakeLists.txt use it to prepare the build + set(PATCH_FILE ${BUILD_DIR}/${id}/tmp/patch.cmake) + if(EXISTS ${dir}/CMakeLists.txt) + file(APPEND ${PATCH_FILE} + "file(COPY ${dir}/CMakeLists.txt + DESTINATION ${BUILD_DIR}/${id}/src/${id})\n") + endif() + + # check if we have patches to apply + file(GLOB patches ${dir}/*.patch) + list(SORT patches) + foreach(patch ${patches}) + file(APPEND ${PATCH_FILE} + "execute_process(COMMAND patch -p1 -i ${patch})\n") + endforeach() + + + # if there's an install.txt use it to properly install the built files + set(INSTALL_COMMAND "") + if(EXISTS ${dir}/install.txt) + set(INSTALL_COMMAND INSTALL_COMMAND ${CMAKE_COMMAND} + -DINPUTDIR=${BUILD_DIR}/${id}/src/${id}-build/ + -DINPUTFILE=${dir}/install.txt + -DDESTDIR=${OUTPUT_DIR} + -DENABLE_STATIC=1 + "${extraflags}" + -P ${PROJECT_SOURCE_DIR}/install.cmake) + elseif(EXISTS ${dir}/noinstall.txt) + set(INSTALL_COMMAND INSTALL_COMMAND "") + endif() + + # check if there's a deps.txt containing dependencies on other libraries + if(EXISTS ${dir}/deps.txt) + file(STRINGS ${dir}/deps.txt deps) + message(STATUS "${id} depends: ${deps}") + else() + set(deps) + endif() + + if(CROSS_AUTOCONF AND AUTOCONF_FILES) + foreach(afile ${AUTOCONF_FILES}) + file(APPEND ${PATCH_FILE} + "message(STATUS \"AUTOCONF: copying ${afile} to ${BUILD_DIR}/${id}/src/${id}\")\n + file(COPY ${afile} DESTINATION ${BUILD_DIR}/${id}/src/${id})\n") + endforeach() + endif() + + # if the patch file exists we need to set the PATCH_COMMAND + set(PATCH_COMMAND "") + if (EXISTS ${PATCH_FILE}) + set(PATCH_COMMAND ${CMAKE_COMMAND} -P ${PATCH_FILE}) + endif() + + # prepare the setup of the call to externalproject_add() + set(EXTERNALPROJECT_SETUP PREFIX ${BUILD_DIR}/${id} + CMAKE_ARGS ${extraflags} ${BUILD_ARGS} + PATCH_COMMAND ${PATCH_COMMAND} + "${INSTALL_COMMAND}") + + # if there's an url defined we need to pass that to externalproject_add() + if(DEFINED url AND NOT "${url}" STREQUAL "") + # check if there's a third parameter in the file + if(deflength GREATER 2) + # the third parameter is considered as a revision of a git repository + list(GET def 2 revision) + + externalproject_add(${id} + GIT_REPOSITORY ${url} + GIT_TAG ${revision} + "${EXTERNALPROJECT_SETUP}") + else() + set(CONFIGURE_COMMAND "") + if(NOT WIN32) + # manually specify the configure command to be able to pass in the custom PKG_CONFIG_PATH + set(CONFIGURE_COMMAND PKG_CONFIG_PATH=${OUTPUT_DIR}/lib/pkgconfig + ${CMAKE_COMMAND} -DCMAKE_LIBRARY_PATH=${OUTPUT_DIR}/lib ${extraflags} ${BUILD_ARGS} + ${BUILD_DIR}/${id}/src/${id} + -DPACKAGE_CONFIG_PATH=${OUTPUT_DIR}/lib/pkgconfig + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DOUTPUT_DIR=${OUTPUT_DIR} + -DCMAKE_PREFIX_PATH=${OUTPUT_DIR} + -DCMAKE_INSTALL_PREFIX=${OUTPUT_DIR} + -DCMAKE_EXE_LINKER_FLAGS=-L${OUTPUT_DIR}/lib + -DCMAKE_INCLUDE_PATH=${OUTPUT_DIR}/include) + endif() + + externalproject_add(${id} + URL ${url} + DOWNLOAD_DIR ${BUILD_DIR}/download + CONFIGURE_COMMAND ${CONFIGURE_COMMAND} + "${EXTERNALPROJECT_SETUP}") + endif() + else() + externalproject_add(${id} + SOURCE_DIR ${dir} + "${EXTERNALPROJECT_SETUP}") + endif() + + if(deps) + add_dependencies(${id} ${deps}) + endif() + endif() + + # if the dependency is available for the target platform add it to the list of the addon's dependencies + # (even if the target already exists as it still has to be built before the addon) + if(${platform_found}) + list(APPEND ${addon}_DEPS ${id}) + endif() + endif() + endforeach() + + # make the ${addon}_DEPS variable available to the calling script + set(${addon}_DEPS "${${addon}_DEPS}" PARENT_SCOPE) +endfunction() + diff --git a/project/cmake/scripts/common/prepare-env.cmake b/project/cmake/scripts/common/prepare-env.cmake new file mode 100644 index 0000000..7df421c --- /dev/null +++ b/project/cmake/scripts/common/prepare-env.cmake @@ -0,0 +1,88 @@ +# parse version.txt to get the version info +if(EXISTS "${APP_ROOT}/version.txt") + file(STRINGS "${APP_ROOT}/version.txt" versions) + foreach (version ${versions}) + if(version MATCHES "^VERSION_.*") + string(REGEX MATCH "^[^ ]+" version_name ${version}) + string(REPLACE "${version_name} " "" version_value ${version}) + set(APP_${version_name} "${version_value}") + else() + string(REGEX MATCH "^[^ ]+" name ${version}) + string(REPLACE "${name} " "" value ${version}) + set(${name} "${value}") + endif() + endforeach() +endif() + +# bail if we can't parse versions +if(NOT DEFINED APP_VERSION_MAJOR OR NOT DEFINED APP_VERSION_MINOR) + message(FATAL_ERROR "Could not determine app version! make sure that ${APP_ROOT}/version.txt exists") +endif() + +### copy all the addon binding header files to include/kodi +# make sure include/kodi exists and is empty +set(KODI_LIB_DIR ${DEPENDS_PATH}/lib/kodi) +if(NOT EXISTS "${KODI_LIB_DIR}/") + file(MAKE_DIRECTORY ${KODI_LIB_DIR}) +endif() + +set(KODI_INCLUDE_DIR ${DEPENDS_PATH}/include/kodi) +if(NOT EXISTS "${KODI_INCLUDE_DIR}/") + file(MAKE_DIRECTORY ${KODI_INCLUDE_DIR}) +endif() + +# we still need XBMC_INCLUDE_DIR and XBMC_LIB_DIR for backwards compatibility to xbmc +set(XBMC_LIB_DIR ${DEPENDS_PATH}/lib/xbmc) +if(NOT EXISTS "${XBMC_LIB_DIR}/") + file(MAKE_DIRECTORY ${XBMC_LIB_DIR}) +endif() +set(XBMC_INCLUDE_DIR ${DEPENDS_PATH}/include/xbmc) +if(NOT EXISTS "${XBMC_INCLUDE_DIR}/") + file(MAKE_DIRECTORY ${XBMC_INCLUDE_DIR}) +endif() + +# make sure C++11 is always set +if(NOT WIN32) + string(REGEX MATCH "-std=(gnu|c)\\+\\+11" cxx11flag "${CMAKE_CXX_FLAGS}") + if(NOT cxx11flag) + set(CXX11_SWITCH "-std=c++11") + endif() +endif() + +# kodi-config.cmake.in (further down) expects a "prefix" variable +get_filename_component(prefix "${DEPENDS_PATH}" ABSOLUTE) + +# generate the proper kodi-config.cmake file +configure_file(${APP_ROOT}/project/cmake/kodi-config.cmake.in ${KODI_LIB_DIR}/kodi-config.cmake @ONLY) +# copy cmake helpers to lib/kodi +file(COPY ${APP_ROOT}/project/cmake/scripts/common/addon-helpers.cmake ${APP_ROOT}/project/cmake/scripts/common/addoptions.cmake DESTINATION ${KODI_LIB_DIR}) + +# generate xbmc-config.cmake for backwards compatibility to xbmc +configure_file(${APP_ROOT}/project/cmake/xbmc-config.cmake.in ${XBMC_LIB_DIR}/xbmc-config.cmake @ONLY) + +### copy all the addon binding header files to include/kodi +# parse addon-bindings.mk to get the list of header files to copy +file(STRINGS ${APP_ROOT}/xbmc/addons/addon-bindings.mk bindings) +string(REPLACE "\n" ";" bindings "${bindings}") +foreach(binding ${bindings}) + string(REPLACE " =" ";" binding "${binding}") + string(REPLACE "+=" ";" binding "${binding}") + list(GET binding 1 header) + # copy the header file to include/kodi + file(COPY ${APP_ROOT}/${header} DESTINATION ${KODI_INCLUDE_DIR}) + + # auto-generate header files for backwards compatibility to xbmc with deprecation warning + # but only do it if the file doesn't already exist + get_filename_component(headerfile ${header} NAME) + if (NOT EXISTS "${XBMC_INCLUDE_DIR}/${headerfile}") + file(WRITE ${XBMC_INCLUDE_DIR}/${headerfile} +"#pragma once +#define DEPRECATION_WARNING \"Including xbmc/${headerfile} has been deprecated, please use kodi/${headerfile}\" +#ifdef _MSC_VER + #pragma message(\"WARNING: \" DEPRECATION_WARNING) +#else + #warning DEPRECATION_WARNING +#endif +#include \"kodi/${headerfile}\"") + endif() +endforeach() diff --git a/project/cmake/scripts/windows/c-flag-overrides.cmake b/project/cmake/scripts/windows/c-flag-overrides.cmake new file mode 100644 index 0000000..ab19701 --- /dev/null +++ b/project/cmake/scripts/windows/c-flag-overrides.cmake @@ -0,0 +1,5 @@ +if(MSVC) + set(CMAKE_C_FLAGS "/MP /DWIN32 /D_WINDOWS /W3 /Zi /arch:SSE2") + set(CMAKE_C_FLAGS_DEBUG "/D_DEBUG /MDd /Ob0 /Od /RTC1 /D_HAS_ITERATOR_DEBUGGING=0 /D_SECURE_SCL=0") + set(CMAKE_C_FLAGS_RELEASE "/MD /Ox /Ob2 /Oi /Ot /Oy /GL /DNDEBUG") +endif(MSVC) \ No newline at end of file diff --git a/project/cmake/scripts/windows/cxx-flag-overrides.cmake b/project/cmake/scripts/windows/cxx-flag-overrides.cmake new file mode 100644 index 0000000..ad3a090 --- /dev/null +++ b/project/cmake/scripts/windows/cxx-flag-overrides.cmake @@ -0,0 +1,5 @@ +if(MSVC) + set(CMAKE_CXX_FLAGS "/MP /DWIN32 /D_WINDOWS /W3 /GR /Zi /EHsc /arch:SSE2") + set(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob0 /Od /RTC1 /D_HAS_ITERATOR_DEBUGGING=0 /D_SECURE_SCL=0") + set(CMAKE_CXX_FLAGS_RELEASE "/MD /Ox /Ob2 /Oi /Ot /Oy /GL /DNDEBUG") +endif(MSVC) \ No newline at end of file diff --git a/project/cmake/xbmc-config.cmake.in b/project/cmake/xbmc-config.cmake.in new file mode 100644 index 0000000..73d1c9c --- /dev/null +++ b/project/cmake/xbmc-config.cmake.in @@ -0,0 +1,4 @@ +message(WARNING "find_package(xbmc) has been deprecated, please use find_package(kodi)") + +find_package(kodi REQUIRED) +set(XBMC_INCLUDE_DIR ${KODI_INCLUDE_DIR}) \ No newline at end of file diff --git a/scripts/sync_buildenv.sh b/scripts/sync_buildenv.sh new file mode 100755 index 0000000..24c8176 --- /dev/null +++ b/scripts/sync_buildenv.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +rbase="/root/kodi/xbmc.git" + +for p in \ + addons/library.xbmc.* \ + "project/cmake" \ + "version.txt" \ + "xbmc/addons" \ + "xbmc/cores/dvdplayer/DVDDemuxers" +do + d="$p" + [ -d "$p" ] && d=$(dirname "$p")/ + + #scp -r xbmc:$rbase/$p $d + rsync --inplace -avz --delete -e ssh xbmc:$rbase/$p $d +done diff --git a/scripts/toolchain/android-arm.cmake b/scripts/toolchain/android-arm.cmake new file mode 100644 index 0000000..21315b3 --- /dev/null +++ b/scripts/toolchain/android-arm.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "android") +set(CPU "armeabi-v7a") +set(PLATFORM "") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/x-tools/android-14/bin/arm-linux-androideabi) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "-march=armv7-a -mtune=cortex-a9 -mfloat-abi=softfp -mfpu=neon") +set(CMAKE_CXX_FLAGS "-march=armv7-a -mtune=cortex-a9 -mfloat-abi=softfp -mfpu=neon -frtti") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/scripts/toolchain/ios-arm.cmake b/scripts/toolchain/ios-arm.cmake new file mode 100644 index 0000000..9bb8b0e --- /dev/null +++ b/scripts/toolchain/ios-arm.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "ios") +set(CPU "armv7") +set(PLATFORM "darwin") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/osx/osxcross/target/bin/arm-apple-darwin14) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-clang) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-clang++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "-mcpu=cortex-a8 -mfpu=neon -ftree-vectorize -mfloat-abi=softfp -pipe -Wno-trigraphs -fpascal-strings -O3 -Wreturn-type -Wunused-variable -fmessage-length=0 -gdwarf-2") +set(CMAKE_CXX_FLAGS "-mcpu=cortex-a8 -mfpu=neon -ftree-vectorize -mfloat-abi=softfp -pipe -Wno-trigraphs -fpascal-strings -O3 -Wreturn-type -Wunused-variable -fmessage-length=0 -gdwarf-2 -stdlib=libc++") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/scripts/toolchain/linux-i486.cmake b/scripts/toolchain/linux-i486.cmake new file mode 100644 index 0000000..eb7041a --- /dev/null +++ b/scripts/toolchain/linux-i486.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "linux") +set(CPU "i686") +set(PLATFORM "") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/x-tools/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "-m32") +set(CMAKE_CXX_FLAGS "-m32") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/scripts/toolchain/linux-x86_64.cmake b/scripts/toolchain/linux-x86_64.cmake new file mode 100644 index 0000000..cb93e39 --- /dev/null +++ b/scripts/toolchain/linux-x86_64.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "linux") +set(CPU "x86_64") +set(PLATFORM "") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/x-tools/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/scripts/toolchain/osx-i386.cmake b/scripts/toolchain/osx-i386.cmake new file mode 100644 index 0000000..e2fb927 --- /dev/null +++ b/scripts/toolchain/osx-i386.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "osx") +set(CPU "i386") +set(PLATFORM "darwin") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/osx/osxcross/target/bin/i386-apple-darwin14) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-clang) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-clang++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "-stdlib=libc++") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/scripts/toolchain/osx-x86_64.cmake b/scripts/toolchain/osx-x86_64.cmake new file mode 100644 index 0000000..c14fb06 --- /dev/null +++ b/scripts/toolchain/osx-x86_64.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "osx") +set(CPU "x86_64") +set(PLATFORM "darwin") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/osx/osxcross/target/bin/x86_64-apple-darwin14) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-clang) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-clang++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "-stdlib=libc++") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/scripts/toolchain/rbpi-arm.cmake b/scripts/toolchain/rbpi-arm.cmake new file mode 100644 index 0000000..31c6a94 --- /dev/null +++ b/scripts/toolchain/rbpi-arm.cmake @@ -0,0 +1,58 @@ +set(CMAKE_SYSTEM_VERSION 1) +set(OS "linux") +set(CPU "arm1176jzf-s") +set(PLATFORM "raspberry-pi") +if("${OS}" STREQUAL "linux" OR "${OS}" STREQUAL "android") + set(CMAKE_SYSTEM_NAME Linux) +endif() + +# set special CORE_SYSTEM_NAME +if("${OS}" STREQUAL "android") + set(CORE_SYSTEM_NAME android) +elseif("${OS}" STREQUAL "ios") + set(CORE_SYSTEM_NAME ios) +elseif("${PLATFORM}" STREQUAL "raspberry-pi") + set(CORE_SYSTEM_NAME rbpi) +elseif("${PLATFORM}" STREQUAL "darwin") + set(CORE_SYSTEM_NAME darwin) +endif() + +#set(CMAKE_FIND_ROOT_PATH @CMAKE_FIND_ROOT_PATH@) + +if("${OS}" STREQUAL "ios" OR "${OS}" STREQUAL "osx") + set(CMAKE_SYSTEM_NAME Darwin) + #set(CMAKE_OSX_SYSROOT @use_sdk_path@) + #list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_SYSROOT}/usr) + #set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_sdk_path@/lib) + #set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_sdk_path@/include) +endif() + +#if("${CORE_SYSTEM_NAME}" STREQUAL "rbpi") +# list(APPEND CMAKE_FIND_ROOT_PATH @use_firmware@/opt/vc) +# set(CMAKE_LIBRARY_PATH @CMAKE_FIND_ROOT_PATH@/lib:@use_firmware@/opt/vc/lib) +# set(CMAKE_INCLUDE_PATH @CMAKE_FIND_ROOT_PATH@/include:@use_firmware@/opt/vc/include) +#endif() + +# specify the cross compiler +set(COMPILER_PREFIX /var/www/ud_3/xbmc/x-tools/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi) +set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++) +set(CMAKE_AR ${COMPILER_PREFIX}-ar CACHE FILEPATH "Archiver") +set(CMAKE_LINKER ${COMPILER_PREFIX}-ld CACHE FILEPATH "Linker") +set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib) +set(CMAKE_C_FLAGS "-mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -mabi=aapcs-linux") +set(CMAKE_CXX_FLAGS "-mcpu=arm1176jzf-s -mtune=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp -mabi=aapcs-linux") +set(CMAKE_CPP_FLAGS "") +set(ENV{CFLAGS} ${CMAKE_C_FLAGS}) +set(ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS}) +set(ENV{CPPFLAGS} ${CMAKE_CPP_FLAGS}) + +# search for programs in the build host directories +#set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +#set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +#set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +if(NOT OS STREQUAL "linux") + set(ADDONS_PREFER_STATIC_LIBS ON) +endif() diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..d21c18f --- /dev/null +++ b/version.txt @@ -0,0 +1,11 @@ +APP_NAME Kodi +COMPANY_NAME XBMC-Foundation +WEBSITE http://kodi.tv +VERSION_MAJOR 15 +VERSION_MINOR 0 +VERSION_TAG ALPHA2 +VERSION_CODE 149702 +ADDON_API 14.9.702 + +# Notes: +# Change AC_INIT in configure.in diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp new file mode 100644 index 0000000..2aa849f --- /dev/null +++ b/xbmc/addons/Addon.cpp @@ -0,0 +1,653 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Addon.h" +#include "AddonManager.h" +#include "settings/Settings.h" +#include "filesystem/Directory.h" +#include "filesystem/File.h" +#include "system.h" +#ifdef HAS_PYTHON +#include "interfaces/python/XBPython.h" +#endif +#if defined(TARGET_DARWIN) +#include "../osx/OSXGNUReplacements.h" +#endif +#ifdef TARGET_FREEBSD +#include "freebsd/FreeBSDGNUReplacements.h" +#endif +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "URL.h" +#include "Util.h" +#include +#include +#include + +using XFILE::CDirectory; +using XFILE::CFile; +using namespace std; + +namespace ADDON +{ + +/** + * helper functions + * + */ + +typedef struct +{ + const char* name; + TYPE type; + int pretty; + const char* icon; +} TypeMapping; + +static const TypeMapping types[] = + {{"unknown", ADDON_UNKNOWN, 0, "" }, + {"xbmc.metadata.scraper.albums", ADDON_SCRAPER_ALBUMS, 24016, "DefaultAddonAlbumInfo.png" }, + {"xbmc.metadata.scraper.artists", ADDON_SCRAPER_ARTISTS, 24017, "DefaultAddonArtistInfo.png" }, + {"xbmc.metadata.scraper.movies", ADDON_SCRAPER_MOVIES, 24007, "DefaultAddonMovieInfo.png" }, + {"xbmc.metadata.scraper.musicvideos", ADDON_SCRAPER_MUSICVIDEOS, 24015, "DefaultAddonMusicVideoInfo.png" }, + {"xbmc.metadata.scraper.tvshows", ADDON_SCRAPER_TVSHOWS, 24014, "DefaultAddonTvInfo.png" }, + {"xbmc.metadata.scraper.library", ADDON_SCRAPER_LIBRARY, 24083, "DefaultAddonInfoLibrary.png" }, + {"xbmc.ui.screensaver", ADDON_SCREENSAVER, 24008, "DefaultAddonScreensaver.png" }, + {"xbmc.player.musicviz", ADDON_VIZ, 24010, "DefaultAddonVisualization.png" }, + {"visualization-library", ADDON_VIZ_LIBRARY, 24084, "" }, + {"xbmc.python.pluginsource", ADDON_PLUGIN, 24005, "" }, + {"xbmc.python.script", ADDON_SCRIPT, 24009, "" }, + {"xbmc.python.weather", ADDON_SCRIPT_WEATHER, 24027, "DefaultAddonWeather.png" }, + {"xbmc.python.lyrics", ADDON_SCRIPT_LYRICS, 24013, "DefaultAddonLyrics.png" }, + {"xbmc.python.library", ADDON_SCRIPT_LIBRARY, 24081, "DefaultAddonHelper.png" }, + {"xbmc.python.module", ADDON_SCRIPT_MODULE, 24082, "DefaultAddonLibrary.png" }, + {"xbmc.subtitle.module", ADDON_SUBTITLE_MODULE, 24012, "DefaultAddonSubtitles.png" }, + {"kodi.context.item", ADDON_CONTEXT_ITEM, 24025, "DefaultAddonContextItem.png" }, + {"xbmc.gui.skin", ADDON_SKIN, 166, "DefaultAddonSkin.png" }, + {"xbmc.webinterface", ADDON_WEB_INTERFACE, 199, "DefaultAddonWebSkin.png" }, + {"xbmc.addon.repository", ADDON_REPOSITORY, 24011, "DefaultAddonRepository.png" }, + {"xbmc.pvrclient", ADDON_PVRDLL, 24019, "DefaultAddonPVRClient.png" }, + {"xbmc.addon.video", ADDON_VIDEO, 1037, "DefaultAddonVideo.png" }, + {"xbmc.addon.audio", ADDON_AUDIO, 1038, "DefaultAddonMusic.png" }, + {"xbmc.addon.image", ADDON_IMAGE, 1039, "DefaultAddonPicture.png" }, + {"xbmc.addon.executable", ADDON_EXECUTABLE, 1043, "DefaultAddonProgram.png" }, + {"xbmc.audioencoder", ADDON_AUDIOENCODER, 200, "DefaultAddonAudioEncoder.png" }, + {"xbmc.service", ADDON_SERVICE, 24018, "DefaultAddonService.png" }}; + +const std::string TranslateType(const ADDON::TYPE &type, bool pretty/*=false*/) +{ + for (unsigned int index=0; index < ARRAY_SIZE(types); ++index) + { + const TypeMapping &map = types[index]; + if (type == map.type) + { + if (pretty && map.pretty) + return g_localizeStrings.Get(map.pretty); + else + return map.name; + } + } + return ""; +} + +TYPE TranslateType(const std::string &string) +{ + for (unsigned int index=0; index < ARRAY_SIZE(types); ++index) + { + const TypeMapping &map = types[index]; + if (string == map.name) + return map.type; + } + + return ADDON_UNKNOWN; +} + +const std::string GetIcon(const ADDON::TYPE& type) +{ + for (unsigned int index=0; index < ARRAY_SIZE(types); ++index) + { + const TypeMapping &map = types[index]; + if (type == map.type) + return map.icon; + } + return ""; +} + +#define EMPTY_IF(x,y) \ + { \ + std::string fan=CAddonMgr::Get().GetExtValue(metadata->configuration, x); \ + if (fan == "true") \ + y.clear(); \ + } + +#define SS(x) (x) ? x : "" + +AddonProps::AddonProps(const cp_extension_t *ext) + : id(SS(ext->plugin->identifier)) + , version(SS(ext->plugin->version)) + , minversion(SS(ext->plugin->abi_bw_compatibility)) + , name(SS(ext->plugin->name)) + , path(SS(ext->plugin->plugin_path)) + , author(SS(ext->plugin->provider_name)) + , stars(0) +{ + if (ext->ext_point_id) + type = TranslateType(ext->ext_point_id); + + icon = "icon.png"; + fanart = URIUtils::AddFileToFolder(path, "fanart.jpg"); + changelog = URIUtils::AddFileToFolder(path, "changelog.txt"); + // Grab more detail from the props... + const cp_extension_t *metadata = CAddonMgr::Get().GetExtension(ext->plugin, "xbmc.addon.metadata"); //plugin, "kodi.addon.metadata"); + if (metadata) + { + summary = CAddonMgr::Get().GetTranslatedString(metadata->configuration, "summary"); + description = CAddonMgr::Get().GetTranslatedString(metadata->configuration, "description"); + disclaimer = CAddonMgr::Get().GetTranslatedString(metadata->configuration, "disclaimer"); + license = CAddonMgr::Get().GetExtValue(metadata->configuration, "license"); + std::string language; + language = CAddonMgr::Get().GetExtValue(metadata->configuration, "language"); + if (!language.empty()) + extrainfo.insert(make_pair("language",language)); + broken = CAddonMgr::Get().GetExtValue(metadata->configuration, "broken"); + EMPTY_IF("nofanart",fanart) + EMPTY_IF("noicon",icon) + EMPTY_IF("nochangelog",changelog) + } + BuildDependencies(ext->plugin); +} + +AddonProps::AddonProps(const cp_plugin_info_t *plugin) + : id(SS(plugin->identifier)) + , type(ADDON_UNKNOWN) + , version(SS(plugin->version)) + , minversion(SS(plugin->abi_bw_compatibility)) + , name(SS(plugin->name)) + , path(SS(plugin->plugin_path)) + , author(SS(plugin->provider_name)) + , stars(0) +{ + BuildDependencies(plugin); +} + +void AddonProps::Serialize(CVariant &variant) const +{ + variant["addonid"] = id; + variant["type"] = TranslateType(type); + variant["version"] = version.asString(); + variant["minversion"] = minversion.asString(); + variant["name"] = name; + variant["license"] = license; + variant["summary"] = summary; + variant["description"] = description; + variant["path"] = path; + variant["libname"] = libname; + variant["author"] = author; + variant["source"] = source; + + if (CURL::IsFullPath(icon)) + variant["icon"] = icon; + else + variant["icon"] = URIUtils::AddFileToFolder(path, icon); + + variant["thumbnail"] = variant["icon"]; + variant["disclaimer"] = disclaimer; + variant["changelog"] = changelog; + + if (CURL::IsFullPath(fanart)) + variant["fanart"] = fanart; + else + variant["fanart"] = URIUtils::AddFileToFolder(path, fanart); + + variant["dependencies"] = CVariant(CVariant::VariantTypeArray); + for (ADDONDEPS::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it) + { + CVariant dep(CVariant::VariantTypeObject); + dep["addonid"] = it->first; + dep["version"] = it->second.first.asString(); + dep["optional"] = it->second.second; + variant["dependencies"].push_back(dep); + } + if (broken.empty()) + variant["broken"] = false; + else + variant["broken"] = broken; + variant["extrainfo"] = CVariant(CVariant::VariantTypeArray); + for (InfoMap::const_iterator it = extrainfo.begin(); it != extrainfo.end(); ++it) + { + CVariant info(CVariant::VariantTypeObject); + info["key"] = it->first; + info["value"] = it->second; + variant["extrainfo"].push_back(info); + } + variant["rating"] = stars; +} + +void AddonProps::BuildDependencies(const cp_plugin_info_t *plugin) +{ + if (!plugin) + return; + for (unsigned int i = 0; i < plugin->num_imports; ++i) + dependencies.insert(make_pair(std::string(plugin->imports[i].plugin_id), + make_pair(AddonVersion(SS(plugin->imports[i].version)), plugin->imports[i].optional != 0))); +} + +/** + * CAddon + * + */ + +CAddon::CAddon(const cp_extension_t *ext) + : m_props(ext) +{ + BuildLibName(ext); + Props().libname = m_strLibName; + BuildProfilePath(); + m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml"); + m_enabled = true; + m_hasSettings = true; + m_hasStrings = false; + m_checkedStrings = false; + m_settingsLoaded = false; + m_userSettingsLoaded = false; +} + +CAddon::CAddon(const cp_plugin_info_t *plugin) + : m_props(plugin) +{ + m_enabled = true; + m_hasSettings = false; + m_hasStrings = false; + m_checkedStrings = true; + m_settingsLoaded = false; + m_userSettingsLoaded = false; +} + +CAddon::CAddon(const AddonProps &props) + : m_props(props) +{ + if (props.libname.empty()) BuildLibName(); + else m_strLibName = props.libname; + BuildProfilePath(); + m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml"); + m_enabled = true; + m_hasSettings = true; + m_hasStrings = false; + m_checkedStrings = false; + m_settingsLoaded = false; + m_userSettingsLoaded = false; +} + +CAddon::CAddon(const CAddon &rhs) + : m_props(rhs.Props()), + m_settings(rhs.m_settings) +{ + m_addonXmlDoc = rhs.m_addonXmlDoc; + m_settingsLoaded = rhs.m_settingsLoaded; + m_userSettingsLoaded = rhs.m_userSettingsLoaded; + m_hasSettings = rhs.m_hasSettings; + BuildProfilePath(); + m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml"); + m_strLibName = rhs.m_strLibName; + m_enabled = rhs.Enabled(); + m_hasStrings = false; + m_checkedStrings = false; +} + +AddonPtr CAddon::Clone() const +{ + return AddonPtr(new CAddon(*this)); +} + +bool CAddon::MeetsVersion(const AddonVersion &version) const +{ + return m_props.minversion <= version && version <= m_props.version; +} + +//TODO platform/path crap should be negotiated between the addon and +// the handler for it's type +void CAddon::BuildLibName(const cp_extension_t *extension) +{ + if (!extension) + { + m_strLibName = "default"; + std::string ext; + switch (m_props.type) + { + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + ext = ADDON_SCRAPER_EXT; + break; + case ADDON_SCREENSAVER: + ext = ADDON_SCREENSAVER_EXT; + break; + case ADDON_SKIN: + m_strLibName = "skin.xml"; + return; + case ADDON_VIZ: + ext = ADDON_VIS_EXT; + break; + case ADDON_PVRDLL: + ext = ADDON_PVRDLL_EXT; + break; + case ADDON_SCRIPT: + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SUBTITLE_MODULE: + case ADDON_PLUGIN: + case ADDON_SERVICE: + case ADDON_CONTEXT_ITEM: + ext = ADDON_PYTHON_EXT; + break; + default: + m_strLibName.clear(); + return; + } + // extensions are returned as *.ext + // so remove the asterisk + ext.erase(0,1); + m_strLibName.append(ext); + } + else + { + switch (m_props.type) + { + case ADDON_SCREENSAVER: + case ADDON_SCRIPT: + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + case ADDON_PVRDLL: + case ADDON_PLUGIN: + case ADDON_WEB_INTERFACE: + case ADDON_SERVICE: + case ADDON_REPOSITORY: + case ADDON_AUDIOENCODER: + case ADDON_CONTEXT_ITEM: + { + std::string temp = CAddonMgr::Get().GetExtValue(extension->configuration, "@library"); + m_strLibName = temp; + } + break; + default: + m_strLibName.clear(); + break; + } + } +} + +/** + * Language File Handling + */ +bool CAddon::LoadStrings() +{ + // Path where the language strings reside + std::string chosenPath = URIUtils::AddFileToFolder(m_props.path, "resources/language/"); + + m_hasStrings = m_strings.Load(chosenPath, CSettings::Get().GetString("locale.language")); + return m_checkedStrings = true; +} + +void CAddon::ClearStrings() +{ + // Unload temporary language strings + m_strings.Clear(); + m_hasStrings = false; +} + +std::string CAddon::GetString(uint32_t id) +{ + if (!m_hasStrings && ! m_checkedStrings && !LoadStrings()) + return ""; + + return m_strings.Get(id); +} + +/** + * Settings Handling + */ +bool CAddon::HasSettings() +{ + return LoadSettings(); +} + +bool CAddon::LoadSettings(bool bForce /* = false*/) +{ + if (m_settingsLoaded && !bForce) + return true; + if (!m_hasSettings) + return false; + std::string addonFileName = URIUtils::AddFileToFolder(m_props.path, "resources/settings.xml"); + + if (!m_addonXmlDoc.LoadFile(addonFileName)) + { + if (CFile::Exists(addonFileName)) + CLog::Log(LOGERROR, "Unable to load: %s, Line %d\n%s", addonFileName.c_str(), m_addonXmlDoc.ErrorRow(), m_addonXmlDoc.ErrorDesc()); + m_hasSettings = false; + return false; + } + + // Make sure that the addon XML has the settings element + TiXmlElement *setting = m_addonXmlDoc.RootElement(); + if (!setting || strcmpi(setting->Value(), "settings") != 0) + { + CLog::Log(LOGERROR, "Error loading Settings %s: cannot find root element 'settings'", addonFileName.c_str()); + return false; + } + SettingsFromXML(m_addonXmlDoc, true); + LoadUserSettings(); + m_settingsLoaded = true; + return true; +} + +bool CAddon::HasUserSettings() +{ + if (!LoadSettings()) + return false; + + return m_userSettingsLoaded; +} + +bool CAddon::ReloadSettings() +{ + return LoadSettings(true); +} + +bool CAddon::LoadUserSettings() +{ + m_userSettingsLoaded = false; + CXBMCTinyXML doc; + if (doc.LoadFile(m_userSettingsPath)) + m_userSettingsLoaded = SettingsFromXML(doc); + return m_userSettingsLoaded; +} + +void CAddon::SaveSettings(void) +{ + if (m_settings.empty()) + return; // no settings to save + + // break down the path into directories + std::string strAddon = URIUtils::GetDirectory(m_userSettingsPath); + URIUtils::RemoveSlashAtEnd(strAddon); + std::string strRoot = URIUtils::GetDirectory(strAddon); + URIUtils::RemoveSlashAtEnd(strRoot); + + // create the individual folders + if (!CDirectory::Exists(strRoot)) + CDirectory::Create(strRoot); + if (!CDirectory::Exists(strAddon)) + CDirectory::Create(strAddon); + + // create the XML file + CXBMCTinyXML doc; + SettingsToXML(doc); + doc.SaveFile(m_userSettingsPath); + m_userSettingsLoaded = true; + + CAddonMgr::Get().ReloadSettings(ID());//push the settings changes to the running addon instance +#ifdef HAS_PYTHON + g_pythonParser.OnSettingsChanged(ID()); +#endif +} + +std::string CAddon::GetSetting(const std::string& key) +{ + if (!LoadSettings()) + return ""; // no settings available + + map::const_iterator i = m_settings.find(key); + if (i != m_settings.end()) + return i->second; + return ""; +} + +void CAddon::UpdateSetting(const std::string& key, const std::string& value) +{ + LoadSettings(); + if (key.empty()) return; + m_settings[key] = value; +} + +bool CAddon::SettingsFromXML(const CXBMCTinyXML &doc, bool loadDefaults /*=false */) +{ + if (!doc.RootElement()) + return false; + + if (loadDefaults) + m_settings.clear(); + + const TiXmlElement* category = doc.RootElement()->FirstChildElement("category"); + if (!category) + category = doc.RootElement(); + + bool foundSetting = false; + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const char *id = setting->Attribute("id"); + const char *value = setting->Attribute(loadDefaults ? "default" : "value"); + if (id && value) + { + m_settings[id] = value; + foundSetting = true; + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + return foundSetting; +} + +void CAddon::SettingsToXML(CXBMCTinyXML &doc) const +{ + TiXmlElement node("settings"); + doc.InsertEndChild(node); + for (map::const_iterator i = m_settings.begin(); i != m_settings.end(); ++i) + { + TiXmlElement nodeSetting("setting"); + nodeSetting.SetAttribute("id", i->first.c_str()); + nodeSetting.SetAttribute("value", i->second.c_str()); + doc.RootElement()->InsertEndChild(nodeSetting); + } + doc.SaveFile(m_userSettingsPath); +} + +TiXmlElement* CAddon::GetSettingsXML() +{ + return m_addonXmlDoc.RootElement(); +} + +void CAddon::BuildProfilePath() +{ + m_profile = StringUtils::Format("special://profile/addon_data/%s/", ID().c_str()); +} + +const std::string CAddon::Icon() const +{ + if (CURL::IsFullPath(m_props.icon)) + return m_props.icon; + return URIUtils::AddFileToFolder(m_props.path, m_props.icon); +} + +const std::string CAddon::LibPath() const +{ + return URIUtils::AddFileToFolder(m_props.path, m_strLibName); +} + +AddonVersion CAddon::GetDependencyVersion(const std::string &dependencyID) const +{ + const ADDON::ADDONDEPS &deps = GetDeps(); + ADDONDEPS::const_iterator it = deps.find(dependencyID); + if (it != deps.end()) + return it->second.first; + return AddonVersion("0.0.0"); +} + +/** + * CAddonLibrary + * + */ + +CAddonLibrary::CAddonLibrary(const cp_extension_t *ext) + : CAddon(ext) + , m_addonType(SetAddonType()) +{ +} + +CAddonLibrary::CAddonLibrary(const AddonProps& props) + : CAddon(props) + , m_addonType(SetAddonType()) +{ +} + +AddonPtr CAddonLibrary::Clone() const +{ + return AddonPtr(new CAddonLibrary(*this)); +} + +TYPE CAddonLibrary::SetAddonType() +{ + if (Type() == ADDON_VIZ_LIBRARY) + return ADDON_VIZ; + else + return ADDON_UNKNOWN; +} + +} /* namespace ADDON */ + diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h new file mode 100644 index 0000000..4fbf085 --- /dev/null +++ b/xbmc/addons/Addon.h @@ -0,0 +1,278 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "IAddon.h" +#include "addons/AddonVersion.h" +#include "utils/XBMCTinyXML.h" +#include "guilib/LocalizeStrings.h" +#include "utils/ISerializable.h" +#include + +class TiXmlElement; +class CAddonCallbacksAddon; + +typedef struct cp_plugin_info_t cp_plugin_info_t; +typedef struct cp_extension_t cp_extension_t; + +namespace ADDON +{ + typedef std::vector VECADDONS; + typedef std::vector::iterator IVECADDONS; + +// utils +const std::string TranslateType(const TYPE &type, bool pretty=false); +const std::string GetIcon(const TYPE &type); + TYPE TranslateType(const std::string &string); + +class AddonProps : public ISerializable +{ +public: + AddonProps(const std::string &id, TYPE type, const std::string &versionstr, const std::string &minversionstr) + : id(id) + , type(type) + , version(versionstr) + , minversion(minversionstr) + , stars(0) + { + } + + virtual ~AddonProps() {} + + AddonProps(const cp_extension_t *ext); + AddonProps(const cp_plugin_info_t *plugin); + + bool operator==(const AddonProps &rhs) + { + return (*this).id == rhs.id + && (*this).type == rhs.type + && (*this).version == rhs.version; + } + + void Serialize(CVariant &variant) const; + + std::string id; + TYPE type; + AddonVersion version; + AddonVersion minversion; + std::string name; + std::string license; + std::string summary; + std::string description; + std::string path; + std::string libname; + std::string author; + std::string source; + std::string icon; + std::string disclaimer; + std::string changelog; + std::string fanart; + ADDONDEPS dependencies; + std::string broken; + InfoMap extrainfo; + int stars; +private: + void BuildDependencies(const cp_plugin_info_t *plugin); +}; + +typedef std::vector VECADDONPROPS; + +class CAddon : public IAddon +{ +public: + CAddon(const AddonProps &addonprops); + CAddon(const cp_extension_t *ext); + CAddon(const cp_plugin_info_t *plugin); + virtual ~CAddon() {} + virtual AddonPtr Clone() const; + + /*! \brief Check whether the this addon can be configured or not + \return true if the addon has settings, false otherwise + \sa LoadSettings, LoadUserSettings, SaveSettings, HasUserSettings, GetSetting, UpdateSetting + */ + bool HasSettings(); + + /*! \brief Check whether the user has configured this addon or not + \return true if previously saved settings are found, false otherwise + \sa LoadSettings, LoadUserSettings, SaveSettings, HasSettings, GetSetting, UpdateSetting + */ + bool HasUserSettings(); + + /*! \brief Save any user configured settings + \sa LoadSettings, LoadUserSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting + */ + virtual void SaveSettings(); + + /*! \brief Update a user-configured setting with a new value + \param key the id of the setting to update + \param value the value that the setting should take + \sa LoadSettings, LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting + */ + void UpdateSetting(const std::string& key, const std::string& value); + + /*! \brief Retrieve a particular settings value + If a previously configured user setting is available, we return it's value, else we return the default (if available) + \param key the id of the setting to retrieve + \return the current value of the setting, or the default if the setting has yet to be configured. + \sa LoadSettings, LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, UpdateSetting + */ + virtual std::string GetSetting(const std::string& key); + + TiXmlElement* GetSettingsXML(); + virtual std::string GetString(uint32_t id); + + // properties + TYPE Type() const { return m_props.type; } + bool IsType(TYPE type) const { return type == m_props.type; } + AddonProps Props() const { return m_props; } + AddonProps& Props() { return m_props; } + const std::string ID() const { return m_props.id; } + const std::string Name() const { return m_props.name; } + bool Enabled() const { return m_enabled; } + virtual bool IsInUse() const { return false; }; + const AddonVersion Version() const { return m_props.version; } + const AddonVersion MinVersion() const { return m_props.minversion; } + const std::string Summary() const { return m_props.summary; } + const std::string Description() const { return m_props.description; } + const std::string Path() const { return m_props.path; } + const std::string Profile() const { return m_profile; } + const std::string LibPath() const; + const std::string Author() const { return m_props.author; } + const std::string ChangeLog() const { return m_props.changelog; } + const std::string FanArt() const { return m_props.fanart; } + const std::string Icon() const; + int Stars() const { return m_props.stars; } + const std::string Disclaimer() const { return m_props.disclaimer; } + const InfoMap &ExtraInfo() const { return m_props.extrainfo; } + const ADDONDEPS &GetDeps() const { return m_props.dependencies; } + + /*! \brief get the required version of a dependency. + \param dependencyID the addon ID of the dependency. + \return the version this addon requires. + */ + AddonVersion GetDependencyVersion(const std::string &dependencyID) const; + + /*! \brief return whether or not this addon satisfies the given version requirements + \param version the version to meet. + \return true if min_version <= version <= current_version, false otherwise. + */ + bool MeetsVersion(const AddonVersion &version) const; + virtual bool ReloadSettings(); + + void MarkAsDisabled() { m_enabled = false; } + + /*! \brief callback for when this add-on is disabled. + Use to perform any needed actions (e.g. stop a service) + */ + virtual void OnDisabled() {}; + + /*! \brief callback for when this add-on is enabled. + Use to perform any needed actions (e.g. start a service) + */ + virtual void OnEnabled() {}; + + /*! \brief retrieve the running instance of an add-on if it persists while running. + */ + virtual AddonPtr GetRunningInstance() const { return AddonPtr(); } + + /*! \brief callbacks for special install/uninstall behaviour */ + virtual bool OnPreInstall() { return false; }; + virtual void OnPostInstall(bool restart, bool update, bool modal) {}; + virtual void OnPreUnInstall() {}; + virtual void OnPostUnInstall() {}; + virtual bool CanInstall(const std::string& referer) { return true; } +protected: + friend class CAddonCallbacksAddon; + + CAddon(const CAddon &rhs); // protected as all copying is handled by Clone() + virtual void BuildLibName(const cp_extension_t *ext = NULL); + + /*! \brief Load the default settings and override these with any previously configured user settings + \param bForce force the load of settings even if they are already loaded (reload) + \return true if settings exist, false otherwise + \sa LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting + */ + virtual bool LoadSettings(bool bForce = false); + + /*! \brief Load the user settings + \return true if user settings exist, false otherwise + \sa LoadSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting + */ + bool LoadUserSettings(); + + /*! \brief Parse settings from an XML document + \param doc XML document to parse for settings + \param loadDefaults if true, the default attribute is used and settings are reset prior to parsing, else the value attribute is used. + \return true if settings are loaded, false otherwise + \sa SettingsToXML + */ + bool SettingsFromXML(const CXBMCTinyXML &doc, bool loadDefaults = false); + + /*! \brief Parse settings into an XML document + \param doc XML document to receive the settings + \sa SettingsFromXML + */ + void SettingsToXML(CXBMCTinyXML &doc) const; + + CXBMCTinyXML m_addonXmlDoc; + std::string m_strLibName; + bool m_settingsLoaded; + bool m_userSettingsLoaded; + + virtual void ClearStrings(); +private: + friend class CAddonMgr; + AddonProps m_props; + std::string m_userSettingsPath; + void BuildProfilePath(); + + virtual bool IsAddonLibrary() { return false; } + + void Enable() { LoadStrings(); m_enabled = true; } + void Disable() { m_enabled = false; ClearStrings();} + + virtual bool LoadStrings(); + + bool m_hasStrings; + bool m_checkedStrings; + bool m_hasSettings; + + std::string m_profile; + bool m_enabled; + CLocalizeStrings m_strings; + std::map m_settings; +}; + +class CAddonLibrary : public CAddon +{ +public: + CAddonLibrary(const AddonProps &props); + CAddonLibrary(const cp_extension_t *ext); + + virtual AddonPtr Clone() const; + +private: + virtual bool IsAddonLibrary() { return true; } + TYPE SetAddonType(); + const TYPE m_addonType; // addon type this library enhances +}; + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonCallbacks.cpp b/xbmc/addons/AddonCallbacks.cpp new file mode 100644 index 0000000..8634373 --- /dev/null +++ b/xbmc/addons/AddonCallbacks.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Addon.h" +#include "AddonCallbacks.h" +#include "AddonCallbacksAddon.h" +#include "AddonCallbacksCodec.h" +#include "AddonCallbacksGUI.h" +#include "AddonCallbacksPVR.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/log.h" + +namespace ADDON +{ + +CAddonCallbacks::CAddonCallbacks(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new AddonCB; + m_helperAddon = NULL; + m_helperGUI = NULL; + m_helperPVR = NULL; + m_helperCODEC = NULL; + + m_callbacks->libBasePath = strdup(CSpecialProtocol::TranslatePath("special://xbmcbin/addons").c_str()); + m_callbacks->addonData = this; + m_callbacks->AddOnLib_RegisterMe = CAddonCallbacks::AddOnLib_RegisterMe; + m_callbacks->AddOnLib_UnRegisterMe = CAddonCallbacks::AddOnLib_UnRegisterMe; + m_callbacks->CODECLib_RegisterMe = CAddonCallbacks::CODECLib_RegisterMe; + m_callbacks->CODECLib_UnRegisterMe = CAddonCallbacks::CODECLib_UnRegisterMe; + m_callbacks->GUILib_RegisterMe = CAddonCallbacks::GUILib_RegisterMe; + m_callbacks->GUILib_UnRegisterMe = CAddonCallbacks::GUILib_UnRegisterMe; + m_callbacks->PVRLib_RegisterMe = CAddonCallbacks::PVRLib_RegisterMe; + m_callbacks->PVRLib_UnRegisterMe = CAddonCallbacks::PVRLib_UnRegisterMe; +} + +CAddonCallbacks::~CAddonCallbacks() +{ + delete m_helperAddon; + m_helperAddon = NULL; + delete m_helperCODEC; + m_helperCODEC = NULL; + delete m_helperGUI; + m_helperGUI = NULL; + delete m_helperPVR; + m_helperPVR = NULL; + free((char*)m_callbacks->libBasePath); + delete m_callbacks; + m_callbacks = NULL; +} + +CB_AddOnLib* CAddonCallbacks::AddOnLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperAddon = new CAddonCallbacksAddon(addon->m_addon); + return addon->m_helperAddon->GetCallbacks(); +} + +void CAddonCallbacks::AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperAddon; + addon->m_helperAddon = NULL; +} + +CB_CODECLib* CAddonCallbacks::CODECLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperCODEC = new CAddonCallbacksCodec(addon->m_addon); + return addon->m_helperCODEC->GetCallbacks(); +} + +void CAddonCallbacks::CODECLib_UnRegisterMe(void *addonData, CB_CODECLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperCODEC; + addon->m_helperCODEC = NULL; +} + +CB_GUILib* CAddonCallbacks::GUILib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperGUI = new CAddonCallbacksGUI(addon->m_addon); + return addon->m_helperGUI->GetCallbacks(); +} + +void CAddonCallbacks::GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperGUI; + addon->m_helperGUI = NULL; +} + +CB_PVRLib* CAddonCallbacks::PVRLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperPVR = new CAddonCallbacksPVR(addon->m_addon); + return addon->m_helperPVR->GetCallbacks(); +} + +void CAddonCallbacks::PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperPVR; + addon->m_helperPVR = NULL; +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacks.h b/xbmc/addons/AddonCallbacks.h new file mode 100644 index 0000000..f0902e4 --- /dev/null +++ b/xbmc/addons/AddonCallbacks.h @@ -0,0 +1,466 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include "cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h" +#include "addons/include/xbmc_pvr_types.h" +#include "addons/include/xbmc_codec_types.h" +#include "../../addons/library.xbmc.gui/libXBMC_gui.h" + +#ifdef TARGET_WINDOWS +#ifndef _SSIZE_T_DEFINED +typedef intptr_t ssize_t; +#define _SSIZE_T_DEFINED +#endif // !_SSIZE_T_DEFINED +#endif // TARGET_WINDOWS + +typedef void (*AddOnLogCallback)(void *addonData, const ADDON::addon_log_t loglevel, const char *msg); +typedef void (*AddOnQueueNotification)(void *addonData, const ADDON::queue_msg_t type, const char *msg); +typedef bool (*AddOnWakeOnLan)(const char* mac); +typedef bool (*AddOnGetSetting)(void *addonData, const char *settingName, void *settingValue); +typedef char* (*AddOnUnknownToUTF8)(const char *sourceDest); +typedef char* (*AddOnGetLocalizedString)(const void* addonData, long dwCode); +typedef char* (*AddOnGetDVDMenuLanguage)(const void* addonData); +typedef void (*AddOnFreeString)(const void* addonData, char* str); + +typedef void* (*AddOnOpenFile)(const void* addonData, const char* strFileName, unsigned int flags); +typedef void* (*AddOnOpenFileForWrite)(const void* addonData, const char* strFileName, bool bOverWrite); +typedef ssize_t (*AddOnReadFile)(const void* addonData, void* file, void* lpBuf, size_t uiBufSize); +typedef bool (*AddOnReadFileString)(const void* addonData, void* file, char *szLine, int iLineLength); +typedef ssize_t (*AddOnWriteFile)(const void* addonData, void* file, const void* lpBuf, size_t uiBufSize); +typedef void (*AddOnFlushFile)(const void* addonData, void* file); +typedef int64_t (*AddOnSeekFile)(const void* addonData, void* file, int64_t iFilePosition, int iWhence); +typedef int (*AddOnTruncateFile)(const void* addonData, void* file, int64_t iSize); +typedef int64_t (*AddOnGetFilePosition)(const void* addonData, void* file); +typedef int64_t (*AddOnGetFileLength)(const void* addonData, void* file); +typedef void (*AddOnCloseFile)(const void* addonData, void* file); +typedef int (*AddOnGetFileChunkSize)(const void* addonData, void* file); +typedef bool (*AddOnFileExists)(const void* addonData, const char *strFileName, bool bUseCache); +typedef int (*AddOnStatFile)(const void* addonData, const char *strFileName, struct __stat64* buffer); +typedef bool (*AddOnDeleteFile)(const void* addonData, const char *strFileName); +typedef bool (*AddOnCanOpenDirectory)(const void* addonData, const char* strURL); +typedef bool (*AddOnCreateDirectory)(const void* addonData, const char *strPath); +typedef bool (*AddOnDirectoryExists)(const void* addonData, const char *strPath); +typedef bool (*AddOnRemoveDirectory)(const void* addonData, const char *strPath); + +typedef struct CB_AddOn +{ + AddOnLogCallback Log; + AddOnQueueNotification QueueNotification; + AddOnWakeOnLan WakeOnLan; + AddOnGetSetting GetSetting; + AddOnUnknownToUTF8 UnknownToUTF8; + AddOnGetLocalizedString GetLocalizedString; + AddOnGetDVDMenuLanguage GetDVDMenuLanguage; + AddOnFreeString FreeString; + + AddOnOpenFile OpenFile; + AddOnOpenFileForWrite OpenFileForWrite; + AddOnReadFile ReadFile; + AddOnReadFileString ReadFileString; + AddOnWriteFile WriteFile; + AddOnFlushFile FlushFile; + AddOnSeekFile SeekFile; + AddOnTruncateFile TruncateFile; + AddOnGetFilePosition GetFilePosition; + AddOnGetFileLength GetFileLength; + AddOnCloseFile CloseFile; + AddOnGetFileChunkSize GetFileChunkSize; + AddOnFileExists FileExists; + AddOnStatFile StatFile; + AddOnDeleteFile DeleteFile; + AddOnCanOpenDirectory CanOpenDirectory; + AddOnCreateDirectory CreateDirectory; + AddOnDirectoryExists DirectoryExists; + AddOnRemoveDirectory RemoveDirectory; +} CB_AddOnLib; + +typedef xbmc_codec_t (*CODECGetCodecByName)(const void* addonData, const char* strCodecName); + +typedef struct CB_CODEC +{ + CODECGetCodecByName GetCodecByName; +} CB_CODECLib; + +typedef void (*GUILock)(); +typedef void (*GUIUnlock)(); +typedef int (*GUIGetScreenHeight)(); +typedef int (*GUIGetScreenWidth)(); +typedef int (*GUIGetVideoResolution)(); +typedef GUIHANDLE (*GUIWindow_New)(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); +typedef void (*GUIWindow_Delete)(void *addonData, GUIHANDLE handle); +typedef void (*GUIWindow_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*)(GUIHANDLE handle), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int)); +typedef bool (*GUIWindow_Show)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_Close)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_DoModal)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_SetFocusId)(void *addonData, GUIHANDLE handle, int iControlId); +typedef int (*GUIWindow_GetFocusId)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_SetCoordinateResolution)(void *addonData, GUIHANDLE handle, int res); +typedef void (*GUIWindow_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); +typedef void (*GUIWindow_SetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key, int value); +typedef void (*GUIWindow_SetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key, bool value); +typedef void (*GUIWindow_SetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key, double value); +typedef const char* (*GUIWindow_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); +typedef int (*GUIWindow_GetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key); +typedef bool (*GUIWindow_GetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key); +typedef double (*GUIWindow_GetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key); +typedef void (*GUIWindow_ClearProperties)(void *addonData, GUIHANDLE handle); +typedef int (*GUIWindow_GetListSize)(void *addonData, GUIHANDLE handle); +typedef void (*GUIWindow_ClearList)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_AddItem)(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition); +typedef GUIHANDLE (*GUIWindow_AddStringItem)(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition); +typedef void (*GUIWindow_RemoveItem)(void *addonData, GUIHANDLE handle, int itemPosition); +typedef GUIHANDLE (*GUIWindow_GetListItem)(void *addonData, GUIHANDLE handle, int listPos); +typedef void (*GUIWindow_SetCurrentListPosition)(void *addonData, GUIHANDLE handle, int listPos); +typedef int (*GUIWindow_GetCurrentListPosition)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_GetControl_Spin)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Button)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_RadioButton)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Edit)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Progress)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_RenderAddon)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIWindow_SetControlLabel)(void *addonData, GUIHANDLE handle, int controlId, const char *label); +typedef void (*GUIWindow_MarkDirtyRegion)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Spin_SetVisible)(void *addonData, GUIHANDLE spinhandle, bool yesNo); +typedef void (*GUIControl_Spin_SetText)(void *addonData, GUIHANDLE spinhandle, const char *label); +typedef void (*GUIControl_Spin_Clear)(void *addonData, GUIHANDLE spinhandle); +typedef void (*GUIControl_Spin_AddLabel)(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue); +typedef int (*GUIControl_Spin_GetValue)(void *addonData, GUIHANDLE spinhandle); +typedef void (*GUIControl_Spin_SetValue)(void *addonData, GUIHANDLE spinhandle, int iValue); +typedef void (*GUIControl_RadioButton_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef void (*GUIControl_RadioButton_SetText)(void *addonData, GUIHANDLE handle, const char *label); +typedef void (*GUIControl_RadioButton_SetSelected)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef bool (*GUIControl_RadioButton_IsSelected)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Progress_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_Progress_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Progress_SetInfo)(void *addonData, GUIHANDLE handle, int iInfo); +typedef int (*GUIControl_Progress_GetInfo)(void *addonData, GUIHANDLE handle); +typedef const char* (*GUIControl_Progress_GetDescription)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_GetControl_Slider)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIControl_Slider_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef const char *(*GUIControl_Slider_GetDescription)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetIntRange)(void *addonData, GUIHANDLE handle, int iStart, int iEnd); +typedef void (*GUIControl_Slider_SetIntValue)(void *addonData, GUIHANDLE handle, int iValue); +typedef int (*GUIControl_Slider_GetIntValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetIntInterval)(void *addonData, GUIHANDLE handle, int iInterval); +typedef void (*GUIControl_Slider_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_Slider_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetFloatRange)(void *addonData, GUIHANDLE handle, float fStart, float fEnd); +typedef void (*GUIControl_Slider_SetFloatValue)(void *addonData, GUIHANDLE handle, float fValue); +typedef float (*GUIControl_Slider_GetFloatValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetFloatInterval)(void *addonData, GUIHANDLE handle, float fInterval); +typedef GUIHANDLE (*GUIWindow_GetControl_SettingsSlider)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIControl_SettingsSlider_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef void (*GUIControl_SettingsSlider_SetText)(void *addonData, GUIHANDLE handle, const char *label); +typedef const char *(*GUIControl_SettingsSlider_GetDescription)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetIntRange)(void *addonData, GUIHANDLE handle, int iStart, int iEnd); +typedef void (*GUIControl_SettingsSlider_SetIntValue)(void *addonData, GUIHANDLE handle, int iValue); +typedef int (*GUIControl_SettingsSlider_GetIntValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetIntInterval)(void *addonData, GUIHANDLE handle, int iInterval); +typedef void (*GUIControl_SettingsSlider_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_SettingsSlider_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetFloatRange)(void *addonData, GUIHANDLE handle, float fStart, float fEnd); +typedef void (*GUIControl_SettingsSlider_SetFloatValue)(void *addonData, GUIHANDLE handle, float fValue); +typedef float (*GUIControl_SettingsSlider_GetFloatValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetFloatInterval)(void *addonData, GUIHANDLE handle, float fInterval); +typedef GUIHANDLE (*GUIListItem_Create)(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); +typedef const char* (*GUIListItem_GetLabel)(void *addonData, GUIHANDLE handle); +typedef void (*GUIListItem_SetLabel)(void *addonData, GUIHANDLE handle, const char *label); +typedef const char* (*GUIListItem_GetLabel2)(void *addonData, GUIHANDLE handle); +typedef void (*GUIListItem_SetLabel2)(void *addonData, GUIHANDLE handle, const char *label); +typedef void (*GUIListItem_SetIconImage)(void *addonData, GUIHANDLE handle, const char *image); +typedef void (*GUIListItem_SetThumbnailImage)(void *addonData, GUIHANDLE handle, const char *image); +typedef void (*GUIListItem_SetInfo)(void *addonData, GUIHANDLE handle, const char *info); +typedef void (*GUIListItem_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); +typedef const char* (*GUIListItem_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); +typedef void (*GUIListItem_SetPath)(void *addonData, GUIHANDLE handle, const char *path); +typedef void (*GUIRenderAddon_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); +typedef void (*GUIRenderAddon_Delete)(void *addonData, GUIHANDLE handle); +typedef void (*GUIRenderAddon_MarkDirty)(void *addonData, GUIHANDLE handle); + +typedef bool (*GUIDialog_Keyboard_ShowAndGetInputWithHead)(char &strTextString, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetInput)(char &strTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetNewPasswordWithHead)(char &newPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetNewPassword)(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndVerifyNewPasswordWithHead)(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmpty, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndVerifyNewPassword)(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); +typedef int (*GUIDialog_Keyboard_ShowAndVerifyPassword)(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetFilter)(char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_SendTextToActiveKeyboard)(const char *aTextString, bool closeKeyboard); +typedef bool (*GUIDialog_Keyboard_isKeyboardActivated)(); + +typedef bool (*GUIDialog_Numeric_ShowAndVerifyNewPassword)(char &strNewPassword, unsigned int iMaxStringSize); +typedef int (*GUIDialog_Numeric_ShowAndVerifyPassword)(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries); +typedef bool (*GUIDialog_Numeric_ShowAndVerifyInput)(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput); +typedef bool (*GUIDialog_Numeric_ShowAndGetTime)(tm &time, const char *strHeading); +typedef bool (*GUIDialog_Numeric_ShowAndGetDate)(tm &date, const char *strHeading); +typedef bool (*GUIDialog_Numeric_ShowAndGetIPAddress)(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading); +typedef bool (*GUIDialog_Numeric_ShowAndGetNumber)(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs); +typedef bool (*GUIDialog_Numeric_ShowAndGetSeconds)(char &timeString, unsigned int iMaxStringSize, const char *strHeading); + +typedef bool (*GUIDialog_FileBrowser_ShowAndGetFile)(const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList); + +typedef void (*GUIDialog_OK_ShowAndGetInputSingleText)(const char *heading, const char *text); +typedef void (*GUIDialog_OK_ShowAndGetInputLineText)(const char *heading, const char *line0, const char *line1, const char *line2); + +typedef bool (*GUIDialog_YesNo_ShowAndGetInputSingleText)(const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel); +typedef bool (*GUIDialog_YesNo_ShowAndGetInputLineText)(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel); +typedef bool (*GUIDialog_YesNo_ShowAndGetInputLineButtonText)(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel); + +typedef void (*GUIDialog_TextViewer)(const char *heading, const char *text); + +typedef int (*GUIDialog_Select)(const char *heading, const char *entries[], unsigned int size, int selected); + +typedef struct CB_GUILib +{ + GUILock Lock; + GUIUnlock Unlock; + GUIGetScreenHeight GetScreenHeight; + GUIGetScreenWidth GetScreenWidth; + GUIGetVideoResolution GetVideoResolution; + GUIWindow_New Window_New; + GUIWindow_Delete Window_Delete; + GUIWindow_SetCallbacks Window_SetCallbacks; + GUIWindow_Show Window_Show; + GUIWindow_Close Window_Close; + GUIWindow_DoModal Window_DoModal; + GUIWindow_SetFocusId Window_SetFocusId; + GUIWindow_GetFocusId Window_GetFocusId; + GUIWindow_SetCoordinateResolution Window_SetCoordinateResolution; + GUIWindow_SetProperty Window_SetProperty; + GUIWindow_SetPropertyInt Window_SetPropertyInt; + GUIWindow_SetPropertyBool Window_SetPropertyBool; + GUIWindow_SetPropertyDouble Window_SetPropertyDouble; + GUIWindow_GetProperty Window_GetProperty; + GUIWindow_GetPropertyInt Window_GetPropertyInt; + GUIWindow_GetPropertyBool Window_GetPropertyBool; + GUIWindow_GetPropertyDouble Window_GetPropertyDouble; + GUIWindow_ClearProperties Window_ClearProperties; + GUIWindow_GetListSize Window_GetListSize; + GUIWindow_ClearList Window_ClearList; + GUIWindow_AddItem Window_AddItem; + GUIWindow_AddStringItem Window_AddStringItem; + GUIWindow_RemoveItem Window_RemoveItem; + GUIWindow_GetListItem Window_GetListItem; + GUIWindow_SetCurrentListPosition Window_SetCurrentListPosition; + GUIWindow_GetCurrentListPosition Window_GetCurrentListPosition; + GUIWindow_GetControl_Spin Window_GetControl_Spin; + GUIWindow_GetControl_Button Window_GetControl_Button; + GUIWindow_GetControl_RadioButton Window_GetControl_RadioButton; + GUIWindow_GetControl_Edit Window_GetControl_Edit; + GUIWindow_GetControl_Progress Window_GetControl_Progress; + GUIWindow_GetControl_RenderAddon Window_GetControl_RenderAddon; + GUIWindow_SetControlLabel Window_SetControlLabel; + GUIWindow_MarkDirtyRegion Window_MarkDirtyRegion; + GUIControl_Spin_SetVisible Control_Spin_SetVisible; + GUIControl_Spin_SetText Control_Spin_SetText; + GUIControl_Spin_Clear Control_Spin_Clear; + GUIControl_Spin_AddLabel Control_Spin_AddLabel; + GUIControl_Spin_GetValue Control_Spin_GetValue; + GUIControl_Spin_SetValue Control_Spin_SetValue; + GUIControl_RadioButton_SetVisible Control_RadioButton_SetVisible; + GUIControl_RadioButton_SetText Control_RadioButton_SetText; + GUIControl_RadioButton_SetSelected Control_RadioButton_SetSelected; + GUIControl_RadioButton_IsSelected Control_RadioButton_IsSelected; + GUIControl_Progress_SetPercentage Control_Progress_SetPercentage; + GUIControl_Progress_GetPercentage Control_Progress_GetPercentage; + GUIControl_Progress_SetInfo Control_Progress_SetInfo; + GUIControl_Progress_GetInfo Control_Progress_GetInfo; + GUIControl_Progress_GetDescription Control_Progress_GetDescription; + GUIListItem_Create ListItem_Create; + GUIListItem_GetLabel ListItem_GetLabel; + GUIListItem_SetLabel ListItem_SetLabel; + GUIListItem_GetLabel2 ListItem_GetLabel2; + GUIListItem_SetLabel2 ListItem_SetLabel2; + GUIListItem_SetIconImage ListItem_SetIconImage; + GUIListItem_SetThumbnailImage ListItem_SetThumbnailImage; + GUIListItem_SetInfo ListItem_SetInfo; + GUIListItem_SetProperty ListItem_SetProperty; + GUIListItem_GetProperty ListItem_GetProperty; + GUIListItem_SetPath ListItem_SetPath; + GUIRenderAddon_SetCallbacks RenderAddon_SetCallbacks; + GUIRenderAddon_Delete RenderAddon_Delete; + + GUIWindow_GetControl_Slider Window_GetControl_Slider; + GUIControl_Slider_SetVisible Control_Slider_SetVisible; + GUIControl_Slider_GetDescription Control_Slider_GetDescription; + GUIControl_Slider_SetIntRange Control_Slider_SetIntRange; + GUIControl_Slider_SetIntValue Control_Slider_SetIntValue; + GUIControl_Slider_GetIntValue Control_Slider_GetIntValue; + GUIControl_Slider_SetIntInterval Control_Slider_SetIntInterval; + GUIControl_Slider_SetPercentage Control_Slider_SetPercentage; + GUIControl_Slider_GetPercentage Control_Slider_GetPercentage; + GUIControl_Slider_SetFloatRange Control_Slider_SetFloatRange; + GUIControl_Slider_SetFloatValue Control_Slider_SetFloatValue; + GUIControl_Slider_GetFloatValue Control_Slider_GetFloatValue; + GUIControl_Slider_SetFloatInterval Control_Slider_SetFloatInterval; + + GUIWindow_GetControl_SettingsSlider Window_GetControl_SettingsSlider; + GUIControl_SettingsSlider_SetVisible Control_SettingsSlider_SetVisible; + GUIControl_SettingsSlider_SetText Control_SettingsSlider_SetText; + GUIControl_SettingsSlider_GetDescription Control_SettingsSlider_GetDescription; + GUIControl_SettingsSlider_SetIntRange Control_SettingsSlider_SetIntRange; + GUIControl_SettingsSlider_SetIntValue Control_SettingsSlider_SetIntValue; + GUIControl_SettingsSlider_GetIntValue Control_SettingsSlider_GetIntValue; + GUIControl_SettingsSlider_SetIntInterval Control_SettingsSlider_SetIntInterval; + GUIControl_SettingsSlider_SetPercentage Control_SettingsSlider_SetPercentage; + GUIControl_SettingsSlider_GetPercentage Control_SettingsSlider_GetPercentage; + GUIControl_SettingsSlider_SetFloatRange Control_SettingsSlider_SetFloatRange; + GUIControl_SettingsSlider_SetFloatValue Control_SettingsSlider_SetFloatValue; + GUIControl_SettingsSlider_GetFloatValue Control_SettingsSlider_GetFloatValue; + GUIControl_SettingsSlider_SetFloatInterval Control_SettingsSlider_SetFloatInterval; + + GUIDialog_Keyboard_ShowAndGetInputWithHead Dialog_Keyboard_ShowAndGetInputWithHead; + GUIDialog_Keyboard_ShowAndGetInput Dialog_Keyboard_ShowAndGetInput; + GUIDialog_Keyboard_ShowAndGetNewPasswordWithHead Dialog_Keyboard_ShowAndGetNewPasswordWithHead; + GUIDialog_Keyboard_ShowAndGetNewPassword Dialog_Keyboard_ShowAndGetNewPassword; + GUIDialog_Keyboard_ShowAndVerifyNewPasswordWithHead Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead; + GUIDialog_Keyboard_ShowAndVerifyNewPassword Dialog_Keyboard_ShowAndVerifyNewPassword; + GUIDialog_Keyboard_ShowAndVerifyPassword Dialog_Keyboard_ShowAndVerifyPassword; + GUIDialog_Keyboard_ShowAndGetFilter Dialog_Keyboard_ShowAndGetFilter; + GUIDialog_Keyboard_SendTextToActiveKeyboard Dialog_Keyboard_SendTextToActiveKeyboard; + GUIDialog_Keyboard_isKeyboardActivated Dialog_Keyboard_isKeyboardActivated; + + GUIDialog_Numeric_ShowAndVerifyNewPassword Dialog_Numeric_ShowAndVerifyNewPassword; + GUIDialog_Numeric_ShowAndVerifyPassword Dialog_Numeric_ShowAndVerifyPassword; + GUIDialog_Numeric_ShowAndVerifyInput Dialog_Numeric_ShowAndVerifyInput; + GUIDialog_Numeric_ShowAndGetTime Dialog_Numeric_ShowAndGetTime; + GUIDialog_Numeric_ShowAndGetDate Dialog_Numeric_ShowAndGetDate; + GUIDialog_Numeric_ShowAndGetIPAddress Dialog_Numeric_ShowAndGetIPAddress; + GUIDialog_Numeric_ShowAndGetNumber Dialog_Numeric_ShowAndGetNumber; + GUIDialog_Numeric_ShowAndGetSeconds Dialog_Numeric_ShowAndGetSeconds; + + GUIDialog_FileBrowser_ShowAndGetFile Dialog_FileBrowser_ShowAndGetFile; + + GUIDialog_OK_ShowAndGetInputSingleText Dialog_OK_ShowAndGetInputSingleText; + GUIDialog_OK_ShowAndGetInputLineText Dialog_OK_ShowAndGetInputLineText; + + GUIDialog_YesNo_ShowAndGetInputSingleText Dialog_YesNo_ShowAndGetInputSingleText; + GUIDialog_YesNo_ShowAndGetInputLineText Dialog_YesNo_ShowAndGetInputLineText; + GUIDialog_YesNo_ShowAndGetInputLineButtonText Dialog_YesNo_ShowAndGetInputLineButtonText; + + GUIDialog_TextViewer Dialog_TextViewer; + GUIDialog_Select Dialog_Select; +} CB_GUILib; + +typedef void (*PVRTransferEpgEntry)(void *userData, const ADDON_HANDLE handle, const EPG_TAG *epgentry); +typedef void (*PVRTransferChannelEntry)(void *userData, const ADDON_HANDLE handle, const PVR_CHANNEL *chan); +typedef void (*PVRTransferTimerEntry)(void *userData, const ADDON_HANDLE handle, const PVR_TIMER *timer); +typedef void (*PVRTransferRecordingEntry)(void *userData, const ADDON_HANDLE handle, const PVR_RECORDING *recording); +typedef void (*PVRAddMenuHook)(void *addonData, PVR_MENUHOOK *hook); +typedef void (*PVRRecording)(void *addonData, const char *Name, const char *FileName, bool On); +typedef void (*PVRTriggerChannelUpdate)(void *addonData); +typedef void (*PVRTriggerTimerUpdate)(void *addonData); +typedef void (*PVRTriggerRecordingUpdate)(void *addonData); +typedef void (*PVRTriggerChannelGroupsUpdate)(void *addonData); +typedef void (*PVRTriggerEpgUpdate)(void *addonData, unsigned int iChannelUid); + +typedef void (*PVRTransferChannelGroup)(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group); +typedef void (*PVRTransferChannelGroupMember)(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member); + +typedef void (*PVRFreeDemuxPacket)(void *addonData, DemuxPacket* pPacket); +typedef DemuxPacket* (*PVRAllocateDemuxPacket)(void *addonData, int iDataSize); + +typedef struct CB_PVRLib +{ + PVRTransferEpgEntry TransferEpgEntry; + PVRTransferChannelEntry TransferChannelEntry; + PVRTransferTimerEntry TransferTimerEntry; + PVRTransferRecordingEntry TransferRecordingEntry; + PVRAddMenuHook AddMenuHook; + PVRRecording Recording; + PVRTriggerChannelUpdate TriggerChannelUpdate; + PVRTriggerTimerUpdate TriggerTimerUpdate; + PVRTriggerRecordingUpdate TriggerRecordingUpdate; + PVRTriggerChannelGroupsUpdate TriggerChannelGroupsUpdate; + PVRTriggerEpgUpdate TriggerEpgUpdate; + PVRFreeDemuxPacket FreeDemuxPacket; + PVRAllocateDemuxPacket AllocateDemuxPacket; + PVRTransferChannelGroup TransferChannelGroup; + PVRTransferChannelGroupMember TransferChannelGroupMember; + +} CB_PVRLib; + + +typedef CB_AddOnLib* (*XBMCAddOnLib_RegisterMe)(void *addonData); +typedef void (*XBMCAddOnLib_UnRegisterMe)(void *addonData, CB_AddOnLib *cbTable); +typedef CB_CODECLib* (*XBMCCODECLib_RegisterMe)(void *addonData); +typedef void (*XBMCCODECLib_UnRegisterMe)(void *addonData, CB_CODECLib *cbTable); +typedef CB_GUILib* (*XBMCGUILib_RegisterMe)(void *addonData); +typedef void (*XBMCGUILib_UnRegisterMe)(void *addonData, CB_GUILib *cbTable); +typedef CB_PVRLib* (*XBMCPVRLib_RegisterMe)(void *addonData); +typedef void (*XBMCPVRLib_UnRegisterMe)(void *addonData, CB_PVRLib *cbTable); + +typedef struct AddonCB +{ + const char *libBasePath; ///> Never, never change this!!! + void *addonData; + XBMCAddOnLib_RegisterMe AddOnLib_RegisterMe; + XBMCAddOnLib_UnRegisterMe AddOnLib_UnRegisterMe; + XBMCCODECLib_RegisterMe CODECLib_RegisterMe; + XBMCCODECLib_UnRegisterMe CODECLib_UnRegisterMe; + XBMCGUILib_RegisterMe GUILib_RegisterMe; + XBMCGUILib_UnRegisterMe GUILib_UnRegisterMe; + XBMCPVRLib_RegisterMe PVRLib_RegisterMe; + XBMCPVRLib_UnRegisterMe PVRLib_UnRegisterMe; +} AddonCB; + + +namespace ADDON +{ + +class CAddon; +class CAddonCallbacksAddon; +class CAddonCallbacksCodec; +class CAddonCallbacksGUI; +class CAddonCallbacksPVR; + +class CAddonCallbacks +{ +public: + CAddonCallbacks(CAddon* addon); + ~CAddonCallbacks(); + AddonCB *GetCallbacks() { return m_callbacks; } + + static CB_AddOnLib* AddOnLib_RegisterMe(void *addonData); + static void AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable); + static CB_CODECLib* CODECLib_RegisterMe(void *addonData); + static void CODECLib_UnRegisterMe(void *addonData, CB_CODECLib *cbTable); + static CB_GUILib* GUILib_RegisterMe(void *addonData); + static void GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable); + static CB_PVRLib* PVRLib_RegisterMe(void *addonData); + static void PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable); + + CAddonCallbacksAddon *GetHelperAddon() { return m_helperAddon; } + CAddonCallbacksCodec *GetHelperCodec() { return m_helperCODEC; } + CAddonCallbacksGUI *GetHelperGUI() { return m_helperGUI; } + CAddonCallbacksPVR *GetHelperPVR() { return m_helperPVR; } + +private: + AddonCB *m_callbacks; + CAddon *m_addon; + CAddonCallbacksAddon *m_helperAddon; + CAddonCallbacksCodec *m_helperCODEC; + CAddonCallbacksGUI *m_helperGUI; + CAddonCallbacksPVR *m_helperPVR; +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksAddon.cpp b/xbmc/addons/AddonCallbacksAddon.cpp new file mode 100644 index 0000000..e54aebe --- /dev/null +++ b/xbmc/addons/AddonCallbacksAddon.cpp @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Application.h" +#include "Addon.h" +#include "AddonCallbacksAddon.h" +#include "utils/log.h" +#include "LangInfo.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "filesystem/File.h" +#include "filesystem/Directory.h" +#include "utils/URIUtils.h" +#include "FileItem.h" +#include "network/Network.h" +#include "utils/CharsetConverter.h" +#include "utils/StringUtils.h" +#include "utils/XMLUtils.h" +#include "cores/dvdplayer/DVDCodecs/DVDCodecs.h" + +using namespace XFILE; + +namespace ADDON +{ + +CAddonCallbacksAddon::CAddonCallbacksAddon(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_AddOnLib; + + /* write XBMC addon-on specific add-on function addresses to the callback table */ + m_callbacks->Log = AddOnLog; + m_callbacks->QueueNotification = QueueNotification; + m_callbacks->WakeOnLan = WakeOnLan; + m_callbacks->GetSetting = GetAddonSetting; + m_callbacks->UnknownToUTF8 = UnknownToUTF8; + m_callbacks->GetLocalizedString = GetLocalizedString; + m_callbacks->GetDVDMenuLanguage = GetDVDMenuLanguage; + m_callbacks->FreeString = FreeString; + + m_callbacks->OpenFile = OpenFile; + m_callbacks->OpenFileForWrite = OpenFileForWrite; + m_callbacks->ReadFile = ReadFile; + m_callbacks->ReadFileString = ReadFileString; + m_callbacks->WriteFile = WriteFile; + m_callbacks->FlushFile = FlushFile; + m_callbacks->SeekFile = SeekFile; + m_callbacks->TruncateFile = TruncateFile; + m_callbacks->GetFilePosition = GetFilePosition; + m_callbacks->GetFileLength = GetFileLength; + m_callbacks->CloseFile = CloseFile; + m_callbacks->GetFileChunkSize = GetFileChunkSize; + m_callbacks->FileExists = FileExists; + m_callbacks->StatFile = StatFile; + m_callbacks->DeleteFile = DeleteFile; + + m_callbacks->CanOpenDirectory = CanOpenDirectory; + m_callbacks->CreateDirectory = CreateDirectory; + m_callbacks->DirectoryExists = DirectoryExists; + m_callbacks->RemoveDirectory = RemoveDirectory; +} + +CAddonCallbacksAddon::~CAddonCallbacksAddon() +{ + /* delete the callback table */ + delete m_callbacks; +} + +void CAddonCallbacksAddon::AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strMessage == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + int xbmcLogLevel = LOGNONE; + switch (addonLogLevel) + { + case LOG_ERROR: + xbmcLogLevel = LOGERROR; + break; + case LOG_INFO: + xbmcLogLevel = LOGINFO; + break; + case LOG_NOTICE: + xbmcLogLevel = LOGNOTICE; + break; + case LOG_DEBUG: + default: + xbmcLogLevel = LOGDEBUG; + break; + } + + std::string strXbmcMessage = StringUtils::Format("AddOnLog: %s: %s", addonHelper->m_addon->Name().c_str(), strMessage); + CLog::Log(xbmcLogLevel, "%s", strXbmcMessage.c_str()); + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } +} + +void CAddonCallbacksAddon::QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strMessage == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + switch (type) + { + case QUEUE_WARNING: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, addonHelper->m_addon->Name(), strMessage, 3000, true); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Warning Message: '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + + case QUEUE_ERROR: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, addonHelper->m_addon->Name(), strMessage, 3000, true); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Error Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + + case QUEUE_INFO: + default: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, addonHelper->m_addon->Name(), strMessage, 3000, false); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Info Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + } + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } +} + +bool CAddonCallbacksAddon::WakeOnLan(const char *mac) +{ + return g_application.getNetwork().WakeOnLan(mac); +} + +bool CAddonCallbacksAddon::GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strSettingName == NULL || settingValue == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return false; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - add-on '%s' requests setting '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strSettingName); + + if (!addonHelper->m_addon->ReloadSettings()) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - could't get settings for add-on '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str()); + return false; + } + + const TiXmlElement *category = addonHelper->m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = addonHelper->m_addon->GetSettingsXML(); + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + + if (id == strSettingName && !type.empty()) + { + if (type == "text" || type == "ipaddress" || + type == "folder" || type == "action" || + type == "music" || type == "pictures" || + type == "programs" || type == "fileenum" || + type == "file") + { + strcpy((char*) settingValue, addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else if (type == "number" || type == "enum" || + type == "labelenum") + { + *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else if (type == "bool") + { + *(bool*) settingValue = (bool) (addonHelper->m_addon->GetSetting(id) == "true" ? true : false); + return true; + } + else if (type == "slider") + { + const char *option = setting->Attribute("option"); + if (option && strcmpi(option, "int") == 0) + { + *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else + { + *(float*) settingValue = (float) atof(addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + } + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - can't find setting '%s' in '%s'", __FUNCTION__, strSettingName, addonHelper->m_addon->Name().c_str()); + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } + + return false; +} + +char* CAddonCallbacksAddon::UnknownToUTF8(const char *strSource) +{ + std::string string; + if (strSource != NULL) + g_charsetConverter.unknownToUTF8(strSource, string); + else + string = ""; + char* buffer = strdup(string.c_str()); + return buffer; +} + +char* CAddonCallbacksAddon::GetLocalizedString(const void* addonData, long dwCode) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || g_application.m_bStop) + return NULL; + + CAddonCallbacksAddon* addonHelper = helper->GetHelperAddon(); + + std::string string; + if (dwCode >= 30000 && dwCode <= 30999) + string = addonHelper->m_addon->GetString(dwCode).c_str(); + else if (dwCode >= 32000 && dwCode <= 32999) + string = addonHelper->m_addon->GetString(dwCode).c_str(); + else + string = g_localizeStrings.Get(dwCode).c_str(); + + char* buffer = strdup(string.c_str()); + return buffer; +} + +char* CAddonCallbacksAddon::GetDVDMenuLanguage(const void* addonData) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + std::string string = g_langInfo.GetDVDMenuLanguage(); + + char* buffer = strdup(string.c_str()); + return buffer; +} + +void CAddonCallbacksAddon::FreeString(const void* addonData, char* str) +{ + free(str); +} + +void* CAddonCallbacksAddon::OpenFile(const void* addonData, const char* strFileName, unsigned int flags) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CFile* file = new CFile; + if (file->Open(strFileName, flags)) + return ((void*)file); + + delete file; + return NULL; +} + +void* CAddonCallbacksAddon::OpenFileForWrite(const void* addonData, const char* strFileName, bool bOverwrite) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CFile* file = new CFile; + if (file->OpenForWrite(strFileName, bOverwrite)) + return ((void*)file); + + delete file; + return NULL; +} + +ssize_t CAddonCallbacksAddon::ReadFile(const void* addonData, void* file, void* lpBuf, size_t uiBufSize) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->Read(lpBuf, uiBufSize); +} + +bool CAddonCallbacksAddon::ReadFileString(const void* addonData, void* file, char *szLine, int iLineLength) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CFile* cfile = (CFile*)file; + if (!cfile) + return false; + + return cfile->ReadString(szLine, iLineLength); +} + +ssize_t CAddonCallbacksAddon::WriteFile(const void* addonData, void* file, const void* lpBuf, size_t uiBufSize) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + CFile* cfile = (CFile*)file; + if (!cfile) + return -1; + + return cfile->Write(lpBuf, uiBufSize); +} + +void CAddonCallbacksAddon::FlushFile(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CFile* cfile = (CFile*)file; + if (!cfile) + return; + + cfile->Flush(); +} + +int64_t CAddonCallbacksAddon::SeekFile(const void* addonData, void* file, int64_t iFilePosition, int iWhence) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->Seek(iFilePosition, iWhence); +} + +int CAddonCallbacksAddon::TruncateFile(const void* addonData, void* file, int64_t iSize) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->Truncate(iSize); +} + +int64_t CAddonCallbacksAddon::GetFilePosition(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->GetPosition(); +} + +int64_t CAddonCallbacksAddon::GetFileLength(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->GetLength(); +} + +void CAddonCallbacksAddon::CloseFile(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CFile* cfile = (CFile*)file; + if (cfile) + { + cfile->Close(); + delete cfile; + } +} + +int CAddonCallbacksAddon::GetFileChunkSize(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->GetChunkSize(); +} + +bool CAddonCallbacksAddon::FileExists(const void* addonData, const char *strFileName, bool bUseCache) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CFile::Exists(strFileName, bUseCache); +} + +int CAddonCallbacksAddon::StatFile(const void* addonData, const char *strFileName, struct __stat64* buffer) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + return CFile::Stat(strFileName, buffer); +} + +bool CAddonCallbacksAddon::DeleteFile(const void* addonData, const char *strFileName) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CFile::Delete(strFileName); +} + +bool CAddonCallbacksAddon::CanOpenDirectory(const void* addonData, const char* strURL) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CFileItemList items; + return CDirectory::GetDirectory(strURL, items); +} + +bool CAddonCallbacksAddon::CreateDirectory(const void* addonData, const char *strPath) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CDirectory::Create(strPath); +} + +bool CAddonCallbacksAddon::DirectoryExists(const void* addonData, const char *strPath) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CDirectory::Exists(strPath); +} + +bool CAddonCallbacksAddon::RemoveDirectory(const void* addonData, const char *strPath) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + // Empty directory + CFileItemList fileItems; + CDirectory::GetDirectory(strPath, fileItems); + for (int i = 0; i < fileItems.Size(); ++i) + CFile::Delete(fileItems.Get(i)->GetPath()); + + return CDirectory::Remove(strPath); +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksAddon.h b/xbmc/addons/AddonCallbacksAddon.h new file mode 100644 index 0000000..6eed7cd --- /dev/null +++ b/xbmc/addons/AddonCallbacksAddon.h @@ -0,0 +1,73 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "AddonCallbacks.h" + +namespace ADDON +{ + +class CAddonCallbacksAddon +{ +public: + CAddonCallbacksAddon(CAddon* addon); + ~CAddonCallbacksAddon(); + + /*! + * @return The callback table. + */ + CB_AddOnLib *GetCallbacks() { return m_callbacks; } + + static void AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage); + static bool GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue); + static void QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage); + static bool WakeOnLan(const char *mac); + static char* UnknownToUTF8(const char *strSource); + static char* GetLocalizedString(const void* addonData, long dwCode); + static char* GetDVDMenuLanguage(const void* addonData); + static void FreeString(const void* addonData, char* str); + + // file operations + static void* OpenFile(const void* addonData, const char* strFileName, unsigned int flags); + static void* OpenFileForWrite(const void* addonData, const char* strFileName, bool bOverwrite); + static ssize_t ReadFile(const void* addonData, void* file, void* lpBuf, size_t uiBufSize); + static bool ReadFileString(const void* addonData, void* file, char *szLine, int iLineLength); + static ssize_t WriteFile(const void* addonData, void* file, const void* lpBuf, size_t uiBufSize); + static void FlushFile(const void* addonData, void* file); + static int64_t SeekFile(const void* addonData, void* file, int64_t iFilePosition, int iWhence); + static int TruncateFile(const void* addonData, void* file, int64_t iSize); + static int64_t GetFilePosition(const void* addonData, void* file); + static int64_t GetFileLength(const void* addonData, void* file); + static void CloseFile(const void* addonData, void* file); + static int GetFileChunkSize(const void* addonData, void* file); + static bool FileExists(const void* addonData, const char *strFileName, bool bUseCache); + static int StatFile(const void* addonData, const char *strFileName, struct __stat64* buffer); + static bool DeleteFile(const void* addonData, const char *strFileName); + static bool CanOpenDirectory(const void* addonData, const char* strURL); + static bool CreateDirectory(const void* addonData, const char *strPath); + static bool DirectoryExists(const void* addonData, const char *strPath); + static bool RemoveDirectory(const void* addonData, const char *strPath); + +private: + CB_AddOnLib *m_callbacks; /*!< callback addresses */ + CAddon *m_addon; /*!< the add-on */ +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksCodec.cpp b/xbmc/addons/AddonCallbacksCodec.cpp new file mode 100644 index 0000000..9c7be30 --- /dev/null +++ b/xbmc/addons/AddonCallbacksCodec.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Application.h" +#include "Addon.h" +#include "AddonCallbacksCodec.h" +#include "utils/StringUtils.h" + +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +} + +namespace ADDON +{ +class CCodecIds +{ +public: + virtual ~CCodecIds(void) {} + + static CCodecIds& Get(void) + { + static CCodecIds _instance; + return _instance; + } + + xbmc_codec_t GetCodecByName(const char* strCodecName) + { + xbmc_codec_t retVal = XBMC_INVALID_CODEC; + if (strlen(strCodecName) == 0) + return retVal; + + std::string strUpperCodecName = strCodecName; + StringUtils::ToUpper(strUpperCodecName); + + std::map::const_iterator it = m_lookup.find(strUpperCodecName); + if (it != m_lookup.end()) + retVal = it->second; + + return retVal; + } + +private: + CCodecIds(void) + { + // get ids and names + AVCodec* codec = NULL; + xbmc_codec_t tmp; + while ((codec = av_codec_next(codec))) + { + if (av_codec_is_decoder(codec)) + { + tmp.codec_type = (xbmc_codec_type_t)codec->type; + tmp.codec_id = codec->id; + + std::string strUpperCodecName = codec->name; + StringUtils::ToUpper(strUpperCodecName); + + m_lookup.insert(std::make_pair(strUpperCodecName, tmp)); + } + } + + // teletext is not returned by av_codec_next. we got our own decoder + tmp.codec_type = XBMC_CODEC_TYPE_SUBTITLE; + tmp.codec_id = AV_CODEC_ID_DVB_TELETEXT; + m_lookup.insert(std::make_pair("TELETEXT", tmp)); + + // rds is not returned by av_codec_next. we got our own decoder + tmp.codec_type = XBMC_CODEC_TYPE_RDS; + tmp.codec_id = AV_CODEC_ID_NONE; + m_lookup.insert(std::make_pair("RDS", tmp)); + } + + std::map m_lookup; +}; + +CAddonCallbacksCodec::CAddonCallbacksCodec(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_CODECLib; + + /* write XBMC addon-on specific add-on function addresses to the callback table */ + m_callbacks->GetCodecByName = GetCodecByName; +} + +CAddonCallbacksCodec::~CAddonCallbacksCodec() +{ + /* delete the callback table */ + delete m_callbacks; +} + +xbmc_codec_t CAddonCallbacksCodec::GetCodecByName(const void* addonData, const char* strCodecName) +{ + (void)addonData; + return CCodecIds::Get().GetCodecByName(strCodecName); +} + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonCallbacksCodec.h b/xbmc/addons/AddonCallbacksCodec.h new file mode 100644 index 0000000..5b816fe --- /dev/null +++ b/xbmc/addons/AddonCallbacksCodec.h @@ -0,0 +1,46 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "AddonCallbacks.h" + +namespace ADDON +{ + +class CAddonCallbacksCodec +{ +public: + CAddonCallbacksCodec(CAddon* addon); + ~CAddonCallbacksCodec(); + + /*! + * @return The callback table. + */ + CB_CODECLib *GetCallbacks() { return m_callbacks; } + + static xbmc_codec_t GetCodecByName(const void* addonData, const char* strCodecName); + +private: + CB_CODECLib* m_callbacks; /*!< callback addresses */ + CAddon* m_addon; /*!< the add-on */ +}; + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonCallbacksGUI.cpp b/xbmc/addons/AddonCallbacksGUI.cpp new file mode 100644 index 0000000..3069039 --- /dev/null +++ b/xbmc/addons/AddonCallbacksGUI.cpp @@ -0,0 +1,2281 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Application.h" +#include "ApplicationMessenger.h" +#include "Addon.h" +#include "AddonCallbacksGUI.h" +#include "utils/log.h" +#include "Skin.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "utils/URIUtils.h" +#include "utils/TimeUtils.h" +#include "utils/StringUtils.h" +#include "guilib/GUIWindowManager.h" +#include "input/Key.h" +#include "guilib/TextureManager.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISettingsSliderControl.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIProgressControl.h" +#include "guilib/GUIRenderingControl.h" +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "dialogs/GUIDialogSelect.h" + +#define CONTROL_BTNVIEWASICONS 2 +#define CONTROL_BTNSORTBY 3 +#define CONTROL_BTNSORTASC 4 +#define CONTROL_LABELFILES 12 + +using namespace std; + +namespace ADDON +{ + +static int iXBMCGUILockRef = 0; + +CAddonCallbacksGUI::CAddonCallbacksGUI(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_GUILib; + + /* GUI Helper functions */ + m_callbacks->Lock = CAddonCallbacksGUI::Lock; + m_callbacks->Unlock = CAddonCallbacksGUI::Unlock; + m_callbacks->GetScreenHeight = CAddonCallbacksGUI::GetScreenHeight; + m_callbacks->GetScreenWidth = CAddonCallbacksGUI::GetScreenWidth; + m_callbacks->GetVideoResolution = CAddonCallbacksGUI::GetVideoResolution; + m_callbacks->Window_New = CAddonCallbacksGUI::Window_New; + m_callbacks->Window_Delete = CAddonCallbacksGUI::Window_Delete; + m_callbacks->Window_SetCallbacks = CAddonCallbacksGUI::Window_SetCallbacks; + m_callbacks->Window_Show = CAddonCallbacksGUI::Window_Show; + m_callbacks->Window_Close = CAddonCallbacksGUI::Window_Close; + m_callbacks->Window_DoModal = CAddonCallbacksGUI::Window_DoModal; + m_callbacks->Window_SetFocusId = CAddonCallbacksGUI::Window_SetFocusId; + m_callbacks->Window_GetFocusId = CAddonCallbacksGUI::Window_GetFocusId; + m_callbacks->Window_SetCoordinateResolution = CAddonCallbacksGUI::Window_SetCoordinateResolution; + m_callbacks->Window_SetProperty = CAddonCallbacksGUI::Window_SetProperty; + m_callbacks->Window_SetPropertyInt = CAddonCallbacksGUI::Window_SetPropertyInt; + m_callbacks->Window_SetPropertyBool = CAddonCallbacksGUI::Window_SetPropertyBool; + m_callbacks->Window_SetPropertyDouble = CAddonCallbacksGUI::Window_SetPropertyDouble; + m_callbacks->Window_GetProperty = CAddonCallbacksGUI::Window_GetProperty; + m_callbacks->Window_GetPropertyInt = CAddonCallbacksGUI::Window_GetPropertyInt; + m_callbacks->Window_GetPropertyBool = CAddonCallbacksGUI::Window_GetPropertyBool; + m_callbacks->Window_GetPropertyDouble = CAddonCallbacksGUI::Window_GetPropertyDouble; + m_callbacks->Window_ClearProperties = CAddonCallbacksGUI::Window_ClearProperties; + + m_callbacks->Window_GetListSize = CAddonCallbacksGUI::Window_GetListSize; + m_callbacks->Window_ClearList = CAddonCallbacksGUI::Window_ClearList; + m_callbacks->Window_AddItem = CAddonCallbacksGUI::Window_AddItem; + m_callbacks->Window_AddStringItem = CAddonCallbacksGUI::Window_AddStringItem; + m_callbacks->Window_RemoveItem = CAddonCallbacksGUI::Window_RemoveItem; + m_callbacks->Window_GetListItem = CAddonCallbacksGUI::Window_GetListItem; + m_callbacks->Window_SetCurrentListPosition = CAddonCallbacksGUI::Window_SetCurrentListPosition; + m_callbacks->Window_GetCurrentListPosition = CAddonCallbacksGUI::Window_GetCurrentListPosition; + + m_callbacks->Window_GetControl_Spin = CAddonCallbacksGUI::Window_GetControl_Spin; + m_callbacks->Window_GetControl_Button = CAddonCallbacksGUI::Window_GetControl_Button; + m_callbacks->Window_GetControl_RadioButton = CAddonCallbacksGUI::Window_GetControl_RadioButton; + m_callbacks->Window_GetControl_Edit = CAddonCallbacksGUI::Window_GetControl_Edit; + m_callbacks->Window_GetControl_Progress = CAddonCallbacksGUI::Window_GetControl_Progress; + m_callbacks->Window_GetControl_RenderAddon = CAddonCallbacksGUI::Window_GetControl_RenderAddon; + m_callbacks->Window_GetControl_Slider = CAddonCallbacksGUI::Window_GetControl_Slider; + m_callbacks->Window_GetControl_SettingsSlider= CAddonCallbacksGUI::Window_GetControl_SettingsSlider; + + m_callbacks->Window_SetControlLabel = CAddonCallbacksGUI::Window_SetControlLabel; + m_callbacks->Window_MarkDirtyRegion = CAddonCallbacksGUI::Window_MarkDirtyRegion; + + m_callbacks->Control_Spin_SetVisible = CAddonCallbacksGUI::Control_Spin_SetVisible; + m_callbacks->Control_Spin_SetText = CAddonCallbacksGUI::Control_Spin_SetText; + m_callbacks->Control_Spin_Clear = CAddonCallbacksGUI::Control_Spin_Clear; + m_callbacks->Control_Spin_AddLabel = CAddonCallbacksGUI::Control_Spin_AddLabel; + m_callbacks->Control_Spin_GetValue = CAddonCallbacksGUI::Control_Spin_GetValue; + m_callbacks->Control_Spin_SetValue = CAddonCallbacksGUI::Control_Spin_SetValue; + + m_callbacks->Control_RadioButton_SetVisible = CAddonCallbacksGUI::Control_RadioButton_SetVisible; + m_callbacks->Control_RadioButton_SetText = CAddonCallbacksGUI::Control_RadioButton_SetText; + m_callbacks->Control_RadioButton_SetSelected= CAddonCallbacksGUI::Control_RadioButton_SetSelected; + m_callbacks->Control_RadioButton_IsSelected = CAddonCallbacksGUI::Control_RadioButton_IsSelected; + + m_callbacks->Control_Progress_SetPercentage = CAddonCallbacksGUI::Control_Progress_SetPercentage; + m_callbacks->Control_Progress_GetPercentage = CAddonCallbacksGUI::Control_Progress_GetPercentage; + m_callbacks->Control_Progress_SetInfo = CAddonCallbacksGUI::Control_Progress_SetInfo; + m_callbacks->Control_Progress_GetInfo = CAddonCallbacksGUI::Control_Progress_GetInfo; + m_callbacks->Control_Progress_GetDescription= CAddonCallbacksGUI::Control_Progress_GetDescription; + + m_callbacks->ListItem_Create = CAddonCallbacksGUI::ListItem_Create; + m_callbacks->ListItem_GetLabel = CAddonCallbacksGUI::ListItem_GetLabel; + m_callbacks->ListItem_SetLabel = CAddonCallbacksGUI::ListItem_SetLabel; + m_callbacks->ListItem_GetLabel2 = CAddonCallbacksGUI::ListItem_GetLabel2; + m_callbacks->ListItem_SetLabel2 = CAddonCallbacksGUI::ListItem_SetLabel2; + m_callbacks->ListItem_SetIconImage = CAddonCallbacksGUI::ListItem_SetIconImage; + m_callbacks->ListItem_SetThumbnailImage = CAddonCallbacksGUI::ListItem_SetThumbnailImage; + m_callbacks->ListItem_SetInfo = CAddonCallbacksGUI::ListItem_SetInfo; + m_callbacks->ListItem_SetProperty = CAddonCallbacksGUI::ListItem_SetProperty; + m_callbacks->ListItem_GetProperty = CAddonCallbacksGUI::ListItem_GetProperty; + m_callbacks->ListItem_SetPath = CAddonCallbacksGUI::ListItem_SetPath; + + m_callbacks->RenderAddon_SetCallbacks = CAddonCallbacksGUI::RenderAddon_SetCallbacks; + m_callbacks->RenderAddon_Delete = CAddonCallbacksGUI::RenderAddon_Delete; + + m_callbacks->Control_Slider_SetVisible = CAddonCallbacksGUI::Control_Slider_SetVisible; + m_callbacks->Control_Slider_GetDescription = CAddonCallbacksGUI::Control_Slider_GetDescription; + m_callbacks->Control_Slider_SetIntRange = CAddonCallbacksGUI::Control_Slider_SetIntRange; + m_callbacks->Control_Slider_SetIntValue = CAddonCallbacksGUI::Control_Slider_SetIntValue; + m_callbacks->Control_Slider_GetIntValue = CAddonCallbacksGUI::Control_Slider_GetIntValue; + m_callbacks->Control_Slider_SetIntInterval = CAddonCallbacksGUI::Control_Slider_SetIntInterval; + m_callbacks->Control_Slider_SetPercentage = CAddonCallbacksGUI::Control_Slider_SetPercentage; + m_callbacks->Control_Slider_GetPercentage = CAddonCallbacksGUI::Control_Slider_GetPercentage; + m_callbacks->Control_Slider_SetFloatRange = CAddonCallbacksGUI::Control_Slider_SetFloatRange; + m_callbacks->Control_Slider_SetFloatValue = CAddonCallbacksGUI::Control_Slider_SetFloatValue; + m_callbacks->Control_Slider_GetFloatValue = CAddonCallbacksGUI::Control_Slider_GetFloatValue; + m_callbacks->Control_Slider_SetFloatInterval = CAddonCallbacksGUI::Control_Slider_SetFloatInterval; + + m_callbacks->Control_SettingsSlider_SetVisible = CAddonCallbacksGUI::Control_SettingsSlider_SetVisible; + m_callbacks->Control_SettingsSlider_SetText = CAddonCallbacksGUI::Control_SettingsSlider_SetText; + m_callbacks->Control_SettingsSlider_GetDescription = CAddonCallbacksGUI::Control_SettingsSlider_GetDescription; + m_callbacks->Control_SettingsSlider_SetIntRange = CAddonCallbacksGUI::Control_SettingsSlider_SetIntRange; + m_callbacks->Control_SettingsSlider_SetIntValue = CAddonCallbacksGUI::Control_SettingsSlider_SetIntValue; + m_callbacks->Control_SettingsSlider_GetIntValue = CAddonCallbacksGUI::Control_SettingsSlider_GetIntValue; + m_callbacks->Control_SettingsSlider_SetIntInterval = CAddonCallbacksGUI::Control_SettingsSlider_SetIntInterval; + m_callbacks->Control_SettingsSlider_SetPercentage = CAddonCallbacksGUI::Control_SettingsSlider_SetPercentage; + m_callbacks->Control_SettingsSlider_GetPercentage = CAddonCallbacksGUI::Control_SettingsSlider_GetPercentage; + m_callbacks->Control_SettingsSlider_SetFloatRange = CAddonCallbacksGUI::Control_SettingsSlider_SetFloatRange; + m_callbacks->Control_SettingsSlider_SetFloatValue = CAddonCallbacksGUI::Control_SettingsSlider_SetFloatValue; + m_callbacks->Control_SettingsSlider_GetFloatValue = CAddonCallbacksGUI::Control_SettingsSlider_GetFloatValue; + m_callbacks->Control_SettingsSlider_SetFloatInterval = CAddonCallbacksGUI::Control_SettingsSlider_SetFloatInterval; + + m_callbacks->Dialog_Keyboard_ShowAndGetInputWithHead = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInputWithHead; + m_callbacks->Dialog_Keyboard_ShowAndGetInput = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInput; + m_callbacks->Dialog_Keyboard_ShowAndGetNewPasswordWithHead = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPasswordWithHead; + m_callbacks->Dialog_Keyboard_ShowAndGetNewPassword = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPassword; + m_callbacks->Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead; + m_callbacks->Dialog_Keyboard_ShowAndVerifyNewPassword = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPassword; + m_callbacks->Dialog_Keyboard_ShowAndVerifyPassword = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyPassword; + m_callbacks->Dialog_Keyboard_ShowAndGetFilter = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetFilter; + m_callbacks->Dialog_Keyboard_SendTextToActiveKeyboard = CAddonCallbacksGUI::Dialog_Keyboard_SendTextToActiveKeyboard; + m_callbacks->Dialog_Keyboard_isKeyboardActivated = CAddonCallbacksGUI::Dialog_Keyboard_isKeyboardActivated; + + m_callbacks->Dialog_Numeric_ShowAndVerifyNewPassword = CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyNewPassword; + m_callbacks->Dialog_Numeric_ShowAndVerifyPassword = CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyPassword; + m_callbacks->Dialog_Numeric_ShowAndVerifyInput = CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyInput; + m_callbacks->Dialog_Numeric_ShowAndGetTime = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetTime; + m_callbacks->Dialog_Numeric_ShowAndGetDate = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetDate; + m_callbacks->Dialog_Numeric_ShowAndGetIPAddress = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetIPAddress; + m_callbacks->Dialog_Numeric_ShowAndGetNumber = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetNumber; + m_callbacks->Dialog_Numeric_ShowAndGetSeconds = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetSeconds; + + m_callbacks->Dialog_FileBrowser_ShowAndGetFile = CAddonCallbacksGUI::Dialog_FileBrowser_ShowAndGetFile; + + m_callbacks->Dialog_OK_ShowAndGetInputSingleText = CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputSingleText; + m_callbacks->Dialog_OK_ShowAndGetInputLineText = CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputLineText; + + m_callbacks->Dialog_YesNo_ShowAndGetInputSingleText = CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputSingleText; + m_callbacks->Dialog_YesNo_ShowAndGetInputLineText = CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineText; + m_callbacks->Dialog_YesNo_ShowAndGetInputLineButtonText = CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineButtonText; + + m_callbacks->Dialog_TextViewer = CAddonCallbacksGUI::Dialog_TextViewer; + + m_callbacks->Dialog_Select = CAddonCallbacksGUI::Dialog_Select; +} + +CAddonCallbacksGUI::~CAddonCallbacksGUI() +{ + delete m_callbacks; +} + +void CAddonCallbacksGUI::Lock() +{ + if (iXBMCGUILockRef == 0) g_graphicsContext.Lock(); + iXBMCGUILockRef++; +} + +void CAddonCallbacksGUI::Unlock() +{ + if (iXBMCGUILockRef > 0) + { + iXBMCGUILockRef--; + if (iXBMCGUILockRef == 0) g_graphicsContext.Unlock(); + } +} + +int CAddonCallbacksGUI::GetScreenHeight() +{ + return g_graphicsContext.GetHeight(); +} + +int CAddonCallbacksGUI::GetScreenWidth() +{ + return g_graphicsContext.GetWidth(); +} + +int CAddonCallbacksGUI::GetVideoResolution() +{ + return (int)g_graphicsContext.GetVideoResolution(); +} + +GUIHANDLE CAddonCallbacksGUI::Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + RESOLUTION_INFO res; + std::string strSkinPath; + if (!forceFallback) + { + /* Check to see if the XML file exists in current skin. If not use + fallback path to find a skin for the addon */ + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res); + + if (!XFILE::CFile::Exists(strSkinPath)) + { + /* Check for the matching folder for the skin in the fallback skins folder */ + std::string basePath = URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources"); + basePath = URIUtils::AddFileToFolder(basePath, "skins"); + basePath = URIUtils::AddFileToFolder(basePath, URIUtils::GetFileName(g_SkinInfo->Path())); + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath); + if (!XFILE::CFile::Exists(strSkinPath)) + { + /* Finally fallback to the DefaultSkin as it didn't exist in either the + XBMC Skin folder or the fallback skin folder */ + forceFallback = true; + } + } + } + + if (forceFallback) + { + //FIXME make this static method of current skin? + std::string str("none"); + AddonProps props(str, ADDON_SKIN, str, str); + std::string basePath = URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources"); + basePath = URIUtils::AddFileToFolder(basePath, "skins"); + basePath = URIUtils::AddFileToFolder(basePath, defaultSkin); + props.path = basePath; + + CSkinInfo skinInfo(props); + skinInfo.Start(); + strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res, basePath); + + if (!XFILE::CFile::Exists(strSkinPath)) + { + CLog::Log(LOGERROR, "Window_New: %s/%s - XML File '%s' for Window is missing, contact Developer '%s' of this AddOn", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str(), strSkinPath.c_str(), guiHelper->m_addon->Author().c_str()); + return NULL; + } + } + // window id's 14000 - 14100 are reserved for addons + // get first window id that is not in use + int id = WINDOW_ADDON_START; + // if window 14099 is in use it means addon can't create more windows + Lock(); + if (g_windowManager.GetWindow(WINDOW_ADDON_END)) + { + Unlock(); + CLog::Log(LOGERROR, "Window_New: %s/%s - maximum number of windows reached", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + while(id < WINDOW_ADDON_END && g_windowManager.GetWindow(id) != NULL) id++; + Unlock(); + + CGUIWindow *window; + if (!asDialog) + window = new CGUIAddonWindow(id, strSkinPath, guiHelper->m_addon); + else + window = new CGUIAddonWindowDialog(id, strSkinPath, guiHelper->m_addon); + + Lock(); + g_windowManager.Add(window); + Unlock(); + + window->SetCoordsRes(res); + + return window; +} + +void CAddonCallbacksGUI::Window_Delete(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + Lock(); + // first change to an existing window + if (g_windowManager.GetActiveWindow() == pAddonWindow->m_iWindowId && !g_application.m_bStop) + { + if(g_windowManager.GetWindow(pAddonWindow->m_iOldWindowId)) + g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId); + else // old window does not exist anymore, switch to home + g_windowManager.ActivateWindow(WINDOW_HOME); + } + // Free any window properties + pAddonWindow->ClearProperties(); + // free the window's resources and unload it (free all guicontrols) + pAddonWindow->FreeResources(true); + + g_windowManager.Remove(pAddonWindow->GetID()); + delete pAddonWindow; + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int)) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->m_clientHandle = clienthandle; + pAddonWindow->CBOnInit = initCB; + pAddonWindow->CBOnClick = clickCB; + pAddonWindow->CBOnFocus = focusCB; + pAddonWindow->CBOnAction = onActionCB; + Unlock(); +} + +bool CAddonCallbacksGUI::Window_Show(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + if (pAddonWindow->m_iOldWindowId != pAddonWindow->m_iWindowId && pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow()) + pAddonWindow->m_iOldWindowId = g_windowManager.GetActiveWindow(); + + Lock(); + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->Show(); + else + g_windowManager.ActivateWindow(pAddonWindow->m_iWindowId); + Unlock(); + + return true; +} + +bool CAddonCallbacksGUI::Window_Close(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Close: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pAddonWindow->m_bModal = false; + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->PulseActionEvent(); + else + ((CGUIAddonWindow*)pAddonWindow)->PulseActionEvent(); + + Lock(); + // if it's a dialog, we have to close it a bit different + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->Show(false); + else + g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId); + pAddonWindow->m_iOldWindowId = 0; + + Unlock(); + + return true; +} + +bool CAddonCallbacksGUI::Window_DoModal(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_DoModal: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pAddonWindow->m_bModal = true; + + if (pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow()) + Window_Show(addonData, handle); + + return true; +} + +bool CAddonCallbacksGUI::Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + if(!pWindow->GetControl(iControlId)) + { + CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - Control does not exist in window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + Lock(); + CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS, pAddonWindow->m_iWindowId, iControlId); + pWindow->OnMessage(msg); + Unlock(); + + return true; +} + +int CAddonCallbacksGUI::Window_GetFocusId(void *addonData, GUIHANDLE handle) +{ + int iControlId = -1; + + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return iControlId; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return iControlId; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return iControlId; + + Lock(); + iControlId = pWindow->GetFocusedControlID(); + Unlock(); + + if (iControlId == -1) + { + CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No control in this window has focus", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return iControlId; + } + + return iControlId; +} + +bool CAddonCallbacksGUI::Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + if (res < RES_HDTV_1080i || res > RES_AUTORES) + { + CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - Invalid resolution", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pWindow->SetCoordsRes((RESOLUTION)res); + + return true; +} + +void CAddonCallbacksGUI::Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key || !value) + { + CLog::Log(LOGERROR, "Window_SetProperty: %s/%s - No Window or NULL key or value", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_SetPropertyInt: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_SetPropertyBool: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_SetPropertyDouble: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +const char* CAddonCallbacksGUI::Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetProperty: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return NULL; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + string value = pWindow->GetProperty(lowerKey).asString(); + Unlock(); + + return strdup(value.c_str()); +} + +int CAddonCallbacksGUI::Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetPropertyInt: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return -1; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return -1; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + int value = (int)pWindow->GetProperty(lowerKey).asInteger(); + Unlock(); + + return value; +} + +bool CAddonCallbacksGUI::Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetPropertyBool: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + bool value = pWindow->GetProperty(lowerKey).asBoolean(); + Unlock(); + + return value; +} + +double CAddonCallbacksGUI::Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0.0; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetPropertyDouble: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return 0.0; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return 0.0; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + double value = pWindow->GetProperty(lowerKey).asDouble(); + Unlock(); + + return value; +} + +void CAddonCallbacksGUI::Window_ClearProperties(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_ClearProperties: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + Lock(); + pWindow->ClearProperties(); + Unlock(); +} + +int CAddonCallbacksGUI::Window_GetListSize(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + int listSize = pAddonWindow->GetListSize(); + Unlock(); + + return listSize; +} + +void CAddonCallbacksGUI::Window_ClearList(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->ClearList(); + Unlock(); + + return; +} + +GUIHANDLE CAddonCallbacksGUI::Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle || !item) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CFileItemPtr pItem((CFileItem*)item); + Lock(); + pAddonWindow->AddItem(pItem, itemPosition); + Unlock(); + + return item; +} + +GUIHANDLE CAddonCallbacksGUI::Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle || !itemName) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CFileItemPtr item(new CFileItem(itemName)); + Lock(); + pAddonWindow->AddItem(item, itemPosition); + Unlock(); + + return item.get(); +} + +void CAddonCallbacksGUI::Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->RemoveItem(itemPosition); + Unlock(); + + return; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + CFileItemPtr fi = pAddonWindow->GetListItem(listPos); + if (fi == NULL) + { + Unlock(); + CLog::Log(LOGERROR, "Window_GetListItem: %s/%s - Index out of range", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + Unlock(); + + return fi.get(); +} + +void CAddonCallbacksGUI::Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->SetCurrentListPosition(listPos); + Unlock(); + + return; +} + +int CAddonCallbacksGUI::Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + int listPos = pAddonWindow->GetCurrentListPosition(); + Unlock(); + + return listPos; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SPINEX) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_BUTTON) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RADIO) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_EDIT) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_PROGRESS) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_RenderAddon(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RENDERADDON) + return NULL; + + CGUIAddonRenderingControl *pProxyControl; + pProxyControl = new CGUIAddonRenderingControl((CGUIRenderingControl*)pGUIControl); + return pProxyControl; +} + +void CAddonCallbacksGUI::Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + CGUIMessage msg(GUI_MSG_LABEL_SET, pAddonWindow->m_iWindowId, controlId); + msg.SetLabel(label); + pAddonWindow->OnMessage(msg); +} + +void CAddonCallbacksGUI::Window_MarkDirtyRegion(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + pAddonWindow->MarkDirtyRegion(); +} + +void CAddonCallbacksGUI::Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetText(label); +} + +void CAddonCallbacksGUI::Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->Clear(); +} + +void CAddonCallbacksGUI::Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->AddLabel(label, iValue); +} + +int CAddonCallbacksGUI::Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return -1; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + return pSpin->GetValue(); +} + +void CAddonCallbacksGUI::Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetValue(iValue); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetLabel(label); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetSelected(yesNo); +} + +bool CAddonCallbacksGUI::Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return false; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + return pRadioButton->IsSelected(); +} + +void CAddonCallbacksGUI::Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + pControl->SetInfo(iInfo); +} + +int CAddonCallbacksGUI::Control_Progress_GetInfo(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + return pControl->GetInfo(); +} + +const char* CAddonCallbacksGUI::Control_Progress_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + std::string string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +/* + * GUI slider control callback functions + */ +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Slider(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SLIDER) + return NULL; + + return pGUIControl; +} + +void CAddonCallbacksGUI::Control_Slider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIControl *pControl = (CGUIControl*)handle; + pControl->SetVisible(yesNo); +} + +const char* CAddonCallbacksGUI::Control_Slider_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + std::string string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::Control_Slider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetRange(iStart, iEnd); +} + +void CAddonCallbacksGUI::Control_Slider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_INT); + pControl->SetIntValue(iValue); +} + +int CAddonCallbacksGUI::Control_Slider_GetIntValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + return pControl->GetIntValue(); +} + +void CAddonCallbacksGUI::Control_Slider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetIntInterval(iInterval); +} + +void CAddonCallbacksGUI::Control_Slider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_Slider_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_Slider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetFloatRange(fStart, fEnd); +} + +void CAddonCallbacksGUI::Control_Slider_SetFloatValue(void *addonData, GUIHANDLE handle, float iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetFloatValue(iValue); +} + +float CAddonCallbacksGUI::Control_Slider_GetFloatValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + return pControl->GetFloatValue(); +} + +void CAddonCallbacksGUI::Control_Slider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetFloatInterval(fInterval); +} + +/* + * GUI settings slider control callback functions + */ +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_SettingsSlider(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SETTINGS_SLIDER) + return NULL; + + return pGUIControl; +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIControl *pControl = (CGUIControl*)handle; + pControl->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetText(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetText(label); +} + +const char* CAddonCallbacksGUI::Control_SettingsSlider_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + std::string string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetRange(iStart, iEnd); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_INT); + pControl->SetIntValue(iValue); +} + +int CAddonCallbacksGUI::Control_SettingsSlider_GetIntValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + return pControl->GetIntValue(); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetIntInterval(iInterval); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_SettingsSlider_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetFloatRange(fStart, fEnd); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetFloatValue(void *addonData, GUIHANDLE handle, float fValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetFloatValue(fValue); +} + +float CAddonCallbacksGUI::Control_SettingsSlider_GetFloatValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + return pControl->GetFloatValue(); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetFloatInterval(fInterval); +} + +/* + * GUI list item control callback functions + */ +GUIHANDLE CAddonCallbacksGUI::ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + // create CFileItem + CFileItem *pItem = new CFileItem(); + if (!pItem) + return NULL; + + if (label) + pItem->SetLabel(label); + if (label2) + pItem->SetLabel2(label2); + if (iconImage) + pItem->SetIconImage(iconImage); + if (thumbnailImage) + pItem->SetArt("thumb", thumbnailImage); + if (path) + pItem->SetPath(path); + + return pItem; +} + +const char* CAddonCallbacksGUI::ListItem_GetLabel(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + std::string string = ((CFileItem*)handle)->GetLabel(); + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetLabel(label); +} + +const char* CAddonCallbacksGUI::ListItem_GetLabel2(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + std::string string = ((CFileItem*)handle)->GetLabel2(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetLabel2(label); +} + +void CAddonCallbacksGUI::ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetIconImage(image); +} + +void CAddonCallbacksGUI::ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetArt("thumb", image); +} + +void CAddonCallbacksGUI::ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + +} + +void CAddonCallbacksGUI::ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetProperty(key, value); +} + +const char* CAddonCallbacksGUI::ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + string string = ((CFileItem*)handle)->GetProperty(key).asString(); + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetPath(path); +} + +void CAddonCallbacksGUI::RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonRenderingControl *pAddonControl = (CGUIAddonRenderingControl*)handle; + + Lock(); + pAddonControl->m_clientHandle = clienthandle; + pAddonControl->CBCreate = createCB; + pAddonControl->CBRender = renderCB; + pAddonControl->CBStop = stopCB; + pAddonControl->CBDirty = dirtyCB; + Unlock(); + + pAddonControl->m_pControl->InitCallback(pAddonControl); +} + +void CAddonCallbacksGUI::RenderAddon_Delete(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonRenderingControl *pAddonControl = (CGUIAddonRenderingControl*)handle; + + Lock(); + pAddonControl->Delete(); + Unlock(); +} + +/*! @name GUI Keyboard functions */ +//@{ +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInputWithHead(char &aTextString, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs) +{ + std::string str = &aTextString; + bool bRet = CGUIKeyboardFactory::ShowAndGetInput(str, strHeading, allowEmptyResult, hiddenInput, autoCloseMs); + if (bRet) + strncpy(&aTextString, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInput(char &aTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs) +{ + std::string str = &aTextString; + bool bRet = CGUIKeyboardFactory::ShowAndGetInput(str, allowEmptyResult, autoCloseMs); + if (bRet) + strncpy(&aTextString, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPasswordWithHead(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndGetNewPassword(str, strHeading, allowEmptyResult, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndGetNewPassword(str, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndVerifyNewPassword(str, strHeading, allowEmptyResult, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndVerifyNewPassword(str, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +int CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs) +{ + std::string str = &strPassword; + int iRet = CGUIKeyboardFactory::ShowAndVerifyPassword(str, strHeading, iRetries, autoCloseMs); + if (iRet) + strncpy(&strPassword, str.c_str(), iMaxStringSize); + return iRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetFilter(char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs) +{ + std::string strText = &aTextString; + bool bRet = CGUIKeyboardFactory::ShowAndGetFilter(strText, searching, autoCloseMs); + if (bRet) + strncpy(&aTextString, strText.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_SendTextToActiveKeyboard(const char *aTextString, bool closeKeyboard) +{ + return CGUIKeyboardFactory::SendTextToActiveKeyboard(aTextString, closeKeyboard); +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_isKeyboardActivated() +{ + return CGUIKeyboardFactory::isKeyboardActivated(); +} +//@} + +/*! @name GUI Numeric functions */ +//@{ +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize) +{ + std::string str = &strNewPassword; + bool bRet = CGUIDialogNumeric::ShowAndVerifyNewPassword(str); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +int CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries) +{ + std::string str = &strPassword; + int bRet = CGUIDialogNumeric::ShowAndVerifyPassword(str, strHeading, iRetries); + if (bRet) + strncpy(&strPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyInput(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput) +{ + std::string str = &strPassword; + bool bRet = CGUIDialogNumeric::ShowAndVerifyInput(str, strHeading, bGetUserInput); + if (bRet) + strncpy(&strPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetTime(tm &time, const char *strHeading) +{ + SYSTEMTIME systemTime; + CDateTime dateTime(time); + dateTime.GetAsSystemTime(systemTime); + if (CGUIDialogNumeric::ShowAndGetTime(systemTime, strHeading)) + { + dateTime = systemTime; + dateTime.GetAsTm(time); + return true; + } + return false; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetDate(tm &date, const char *strHeading) +{ + SYSTEMTIME systemTime; + CDateTime dateTime(date); + dateTime.GetAsSystemTime(systemTime); + if (CGUIDialogNumeric::ShowAndGetDate(systemTime, strHeading)) + { + dateTime = systemTime; + dateTime.GetAsTm(date); + return true; + } + return false; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetIPAddress(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading) +{ + std::string strIP = &strIPAddress; + bool bRet = CGUIDialogNumeric::ShowAndGetIPAddress(strIP, strHeading); + if (bRet) + strncpy(&strIPAddress, strIP.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetNumber(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs) +{ + std::string str = &strInput; + bool bRet = CGUIDialogNumeric::ShowAndGetNumber(str, strHeading, iAutoCloseTimeoutMs); + if (bRet) + strncpy(&strInput, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetSeconds(char &timeString, unsigned int iMaxStringSize, const char *strHeading) +{ + std::string str = &timeString; + bool bRet = CGUIDialogNumeric::ShowAndGetSeconds(str, strHeading); + if (bRet) + strncpy(&timeString, str.c_str(), iMaxStringSize); + return bRet; +} +//@} + +/*! @name GUI File browser functions */ +//@{ +bool CAddonCallbacksGUI::Dialog_FileBrowser_ShowAndGetFile(const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList) +{ + std::string strPath = &path; + bool bRet = CGUIDialogFileBrowser::ShowAndGetFile(directory, mask, heading, strPath, useThumbs, useFileDirectories, singleList); + if (bRet) + strncpy(&path, strPath.c_str(), iMaxStringSize); + return bRet; +} +//@} + +/*! @name GUI OK Dialog */ +//@{ +void CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputSingleText(const char *heading, const char *text) +{ + CGUIDialogOK::ShowAndGetInput(heading, text); +} + +void CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2) +{ + CGUIDialogOK::ShowAndGetInput(heading, line0, line1, line2); +} +//@} + +/*! @name GUI Yes No Dialog */ +//@{ +bool CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputSingleText(const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel) +{ + return CGUIDialogYesNo::ShowAndGetInput(heading, text, bCanceled, noLabel, yesLabel); +} + +bool CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel) +{ + return CGUIDialogYesNo::ShowAndGetInput(heading, line0, line1, line2, noLabel, yesLabel); +} + +bool CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineButtonText(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel) +{ + return CGUIDialogYesNo::ShowAndGetInput(heading, line0, line1, line2, bCanceled, noLabel, yesLabel); +} +//@} + +/*! @name GUI Text viewer Dialog */ +//@{ +void CAddonCallbacksGUI::Dialog_TextViewer(const char *heading, const char *text) +{ + CGUIDialogTextViewer* pDialog = (CGUIDialogTextViewer*)g_windowManager.GetWindow(WINDOW_DIALOG_TEXT_VIEWER); + pDialog->SetHeading(heading); + pDialog->SetText(text); + pDialog->DoModal(); +} +//@} + +/*! @name GUI select Dialog */ +//@{ +int CAddonCallbacksGUI::Dialog_Select(const char *heading, const char *entries[], unsigned int size, int selected) +{ + CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + pDialog->Reset(); + pDialog->SetHeading(heading); + + for (unsigned int i = 0; i < size; i++) + pDialog->Add(entries[i]); + + if (selected > 0) + pDialog->SetSelected(selected); + + pDialog->DoModal(); + return pDialog->GetSelectedLabel(); +} +//@} + +CGUIAddonWindow::CGUIAddonWindow(int id, const std::string& strXML, CAddon* addon) + : CGUIMediaWindow(id, strXML.c_str()) + , m_iWindowId(id) + , m_iOldWindowId(0) + , m_bModal(false) + , m_bIsDialog(false) + , m_actionEvent(true) + , m_addon(addon) +{ + m_loadType = LOAD_ON_GUI_INIT; + CBOnInit = NULL; + CBOnFocus = NULL; + CBOnClick = NULL; + CBOnAction = NULL; +} + +CGUIAddonWindow::~CGUIAddonWindow(void) +{ +} + +bool CGUIAddonWindow::OnAction(const CAction &action) +{ + // Let addon decide whether it wants to hande action first + if (CBOnAction && CBOnAction(m_clientHandle, action.GetID())) + return true; + + return CGUIWindow::OnAction(action); +} + +bool CGUIAddonWindow::OnMessage(CGUIMessage& message) +{ + // TODO: We shouldn't be dropping down to CGUIWindow in any of this ideally. + // We have to make up our minds about what python should be doing and + // what this side of things should be doing + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + return CGUIMediaWindow::OnMessage(message); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + CGUIMediaWindow::OnMessage(message); + if (CBOnInit) + CBOnInit(m_clientHandle); + + return true; + } + break; + + case GUI_MSG_SETFOCUS: + { + if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != (int)message.GetControlId()) + { + m_viewControl.SetFocused(); + return true; + } + // check if our focused control is one of our category buttons + int iControl = message.GetControlId(); + if (CBOnFocus) + { + CBOnFocus(m_clientHandle, iControl); + } + } + break; + + case GUI_MSG_FOCUSED: + { + if (HasID(message.GetSenderId()) && CBOnFocus) + { + CBOnFocus(m_clientHandle, message.GetControlId()); + } + } + break; + + case GUI_MSG_CLICKED: + { + int iControl=message.GetSenderId(); + // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4. + if (iControl == CONTROL_BTNSORTASC) // sort asc + { + CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortOrder(); + UpdateFileList();*/ + return true; + } + else if (iControl == CONTROL_BTNSORTBY) // sort by + { + CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortMethod(); + UpdateFileList();*/ + return true; + } + + if (CBOnClick && iControl && iControl != (int)this->GetID()) + { + CGUIControl* controlClicked = (CGUIControl*)this->GetControl(iControl); + + // The old python way used to check list AND SELECITEM method or if its a button, checkmark. + // Its done this way for now to allow other controls without a python version like togglebutton to still raise a onAction event + if (controlClicked) // Will get problems if we the id is not on the window and we try to do GetControlType on it. So check to make sure it exists + { + if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM || + message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) || + !controlClicked->IsContainer()) + { + if (CBOnClick(m_clientHandle, iControl)) + return true; + } + else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK) + { +// PyXBMCAction* inf = new PyXBMCAction; +// inf->pObject = Action_FromAction(CAction(ACTION_CONTEXT_MENU)); +// inf->pCallbackWindow = pCallbackWindow; +// +// // aquire lock? +// PyXBMC_AddPendingCall(Py_XBMC_Event_OnAction, inf); +// PulseActionEvent(); + } + } + } + } + break; + } + + return CGUIMediaWindow::OnMessage(message); +} + +void CGUIAddonWindow::AllocResources(bool forceLoad /*= FALSE */) +{ + std::string tmpDir = URIUtils::GetDirectory(GetProperty("xmlfile").asString()); + std::string fallbackMediaPath; + URIUtils::GetParentPath(tmpDir, fallbackMediaPath); + URIUtils::RemoveSlashAtEnd(fallbackMediaPath); + m_mediaDir = fallbackMediaPath; + + //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: %s", fallbackMediaPath.c_str()); + g_TextureManager.AddTexturePath(m_mediaDir); + CGUIMediaWindow::AllocResources(forceLoad); + g_TextureManager.RemoveTexturePath(m_mediaDir); +} + +void CGUIAddonWindow::FreeResources(bool forceUnLoad /*= FALSE */) +{ + CGUIMediaWindow::FreeResources(forceUnLoad); +} + +void CGUIAddonWindow::Render() +{ + g_TextureManager.AddTexturePath(m_mediaDir); + CGUIMediaWindow::Render(); + g_TextureManager.RemoveTexturePath(m_mediaDir); +} + +void CGUIAddonWindow::Update() +{ +} + +void CGUIAddonWindow::AddItem(CFileItemPtr fileItem, int itemPosition) +{ + if (itemPosition == -1 || itemPosition > m_vecItems->Size()) + { + m_vecItems->Add(fileItem); + } + else if (itemPosition < -1 && !(itemPosition-1 < m_vecItems->Size())) + { + m_vecItems->AddFront(fileItem,0); + } + else + { + m_vecItems->AddFront(fileItem,itemPosition); + } + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +void CGUIAddonWindow::RemoveItem(int itemPosition) +{ + m_vecItems->Remove(itemPosition); + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +int CGUIAddonWindow::GetCurrentListPosition() +{ + return m_viewControl.GetSelectedItem(); +} + +void CGUIAddonWindow::SetCurrentListPosition(int item) +{ + m_viewControl.SetSelectedItem(item); +} + +int CGUIAddonWindow::GetListSize() +{ + return m_vecItems->Size(); +} + +CFileItemPtr CGUIAddonWindow::GetListItem(int position) +{ + if (position < 0 || position >= m_vecItems->Size()) return CFileItemPtr(); + return m_vecItems->Get(position); +} + +void CGUIAddonWindow::ClearList() +{ + ClearFileItems(); + + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +void CGUIAddonWindow::GetContextButtons(int itemNumber, CContextButtons &buttons) +{ + // maybe on day we can make an easy way to do this context menu + // with out this method overriding the MediaWindow version, it will display 'Add to Favorites' +} + +void CGUIAddonWindow::WaitForActionEvent(unsigned int timeout) +{ + m_actionEvent.WaitMSec(timeout); + m_actionEvent.Reset(); +} + +void CGUIAddonWindow::PulseActionEvent() +{ + m_actionEvent.Set(); +} + +bool CGUIAddonWindow::OnClick(int iItem) +{ + // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item + // which if its not media is BAD and 99 out of 100 times undesireable. + return false; +} + +// SetupShares(); +/* + CGUIMediaWindow::OnWindowLoaded() calls SetupShares() so override it +and just call UpdateButtons(); +*/ +void CGUIAddonWindow::SetupShares() +{ + UpdateButtons(); +} + + +CGUIAddonWindowDialog::CGUIAddonWindowDialog(int id, const std::string& strXML, CAddon* addon) +: CGUIAddonWindow(id,strXML,addon) +{ + m_bRunning = false; + m_bIsDialog = true; +} + +CGUIAddonWindowDialog::~CGUIAddonWindowDialog(void) +{ +} + +bool CGUIAddonWindowDialog::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT) + { + CGUIWindow *pWindow = g_windowManager.GetWindow(g_windowManager.GetActiveWindow()); + if (pWindow) + g_windowManager.ShowOverlay(pWindow->GetOverlayState()); + return CGUIWindow::OnMessage(message); + } + return CGUIAddonWindow::OnMessage(message); +} + +void CGUIAddonWindowDialog::Show(bool show /* = true */) +{ + unsigned int iCount = g_graphicsContext.exit(); + ThreadMessage tMsg = {TMSG_GUI_ADDON_DIALOG, 1, show ? 1 : 0}; + tMsg.lpVoid = this; + CApplicationMessenger::Get().SendMessage(tMsg, true); + g_graphicsContext.restore(iCount); +} + +void CGUIAddonWindowDialog::Show_Internal(bool show /* = true */) +{ + if (show) + { + m_bModal = true; + m_bRunning = true; + g_windowManager.RouteToWindow(this); + + // active this window... + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, m_iWindowId); + OnMessage(msg); + + // this dialog is derived from GUiMediaWindow + // make sure it is rendered last + m_renderOrder = 1; + while (m_bRunning && !g_application.m_bStop) + { + g_windowManager.ProcessRenderLoop(); + } + } + else // hide + { + m_bRunning = false; + + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT,0,0); + OnMessage(msg); + + g_windowManager.RemoveDialog(GetID()); + } +} + +CGUIAddonRenderingControl::CGUIAddonRenderingControl(CGUIRenderingControl *pControl) +{ + m_pControl = pControl; + m_refCount = 1; +} + +bool CGUIAddonRenderingControl::Create(int x, int y, int w, int h, void *device) +{ + if (CBCreate) + { + if (CBCreate(m_clientHandle, x, y, w, h, device)) + { + m_refCount++; + return true; + } + } + return false; +} + +void CGUIAddonRenderingControl::Render() +{ + if (CBRender) + { + g_graphicsContext.BeginPaint(); + CBRender(m_clientHandle); + g_graphicsContext.EndPaint(); + } +} + +void CGUIAddonRenderingControl::Stop() +{ + if (CBStop) + { + CBStop(m_clientHandle); + } + m_refCount--; + if (m_refCount <= 0) + delete this; +} + +void CGUIAddonRenderingControl::Delete() +{ + m_refCount--; + if (m_refCount <= 0) + delete this; +} + +bool CGUIAddonRenderingControl::IsDirty() +{ + bool ret = true; + if (CBDirty) + { + ret = CBDirty(m_clientHandle); + } + return ret; +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksGUI.h b/xbmc/addons/AddonCallbacksGUI.h new file mode 100644 index 0000000..ae032a7 --- /dev/null +++ b/xbmc/addons/AddonCallbacksGUI.h @@ -0,0 +1,272 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + + +#include "AddonCallbacks.h" +#include "windows/GUIMediaWindow.h" +#include "threads/Event.h" +#include "guilib/IRenderingCallback.h" + +class CGUISpinControlEx; +class CGUIButtonControl; +class CGUIRadioButtonControl; +class CGUISliderControl; +class CGUISettingsSliderControl; +class CGUIEditControl; +class CGUIRenderingControl; + +namespace ADDON +{ + +class CAddonCallbacksGUI +{ +public: + CAddonCallbacksGUI(CAddon* addon); + ~CAddonCallbacksGUI(); + + /**! \name General Functions */ + CB_GUILib *GetCallbacks() { return m_callbacks; } + + static void Lock(); + static void Unlock(); + static int GetScreenHeight(); + static int GetScreenWidth(); + static int GetVideoResolution(); + + static GUIHANDLE Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + static void Window_Delete(void *addonData, GUIHANDLE handle); + static void Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int)); + static bool Window_Show(void *addonData, GUIHANDLE handle); + static bool Window_Close(void *addonData, GUIHANDLE handle); + static bool Window_DoModal(void *addonData, GUIHANDLE handle); + static bool Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId); + static int Window_GetFocusId(void *addonData, GUIHANDLE handle); + static bool Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res); + static void Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); + static void Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value); + static void Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value); + static void Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value); + static const char * Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key); + static int Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key); + static bool Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key); + static double Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key); + static void Window_ClearProperties(void *addonData, GUIHANDLE handle); + static int Window_GetListSize(void *addonData, GUIHANDLE handle); + static void Window_ClearList(void *addonData, GUIHANDLE handle); + static GUIHANDLE Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition); + static GUIHANDLE Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition); + static void Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition); + static GUIHANDLE Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos); + static void Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos); + static int Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle); + static GUIHANDLE Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_RenderAddon(void *addonData, GUIHANDLE handle, int controlId); + static void Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label); + static void Window_MarkDirtyRegion(void *addonData, GUIHANDLE handle); + static void Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo); + static void Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label); + static void Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle); + static void Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue); + static int Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle); + static void Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue); + static void Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static void Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label); + static void Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo); + static bool Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle); + static void Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo); + static int Control_Progress_GetInfo(void *addonData, GUIHANDLE handle); + static const char * Control_Progress_GetDescription(void *addonData, GUIHANDLE handle); + + static GUIHANDLE Window_GetControl_Slider(void *addonData, GUIHANDLE handle, int controlId); + static void Control_Slider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static const char * Control_Slider_GetDescription(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd); + static void Control_Slider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue); + static int Control_Slider_GetIntValue(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval); + static void Control_Slider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_Slider_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd); + static void Control_Slider_SetFloatValue(void *addonData, GUIHANDLE handle, float fValue); + static float Control_Slider_GetFloatValue(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval); + + static GUIHANDLE Window_GetControl_SettingsSlider(void *addonData, GUIHANDLE handle, int controlId); + static void Control_SettingsSlider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static void Control_SettingsSlider_SetText(void *addonData, GUIHANDLE handle, const char *label); + static const char * Control_SettingsSlider_GetDescription(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd); + static void Control_SettingsSlider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue); + static int Control_SettingsSlider_GetIntValue(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval); + static void Control_SettingsSlider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_SettingsSlider_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd); + static void Control_SettingsSlider_SetFloatValue(void *addonData, GUIHANDLE handle, float fValue); + static float Control_SettingsSlider_GetFloatValue(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval); + + static GUIHANDLE ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + static const char * ListItem_GetLabel(void *addonData, GUIHANDLE handle); + static void ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label); + static const char * ListItem_GetLabel2(void *addonData, GUIHANDLE handle); + static void ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label); + static void ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image); + static void ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image); + static void ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info); + static void ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); + static const char * ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key); + static void ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path); + static void RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); + static void RenderAddon_Delete(void *addonData, GUIHANDLE handle); + static void RenderAddon_MarkDirty(void *addonData, GUIHANDLE handle); + + static bool Dialog_Keyboard_ShowAndGetInput(char &aTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetInputWithHead(char &aTextString, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetNewPasswordWithHead(char &newPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmpty, unsigned int autoCloseMs); + static int Dialog_Keyboard_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetFilter(char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs); + static bool Dialog_Keyboard_SendTextToActiveKeyboard(const char *aTextString, bool closeKeyboard); + static bool Dialog_Keyboard_isKeyboardActivated(); + + static bool Dialog_Numeric_ShowAndVerifyNewPassword(char &strNewPasswor, unsigned int iMaxStringSized); + static int Dialog_Numeric_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries); + static bool Dialog_Numeric_ShowAndVerifyInput(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput); + static bool Dialog_Numeric_ShowAndGetTime(tm &time, const char *strHeading); + static bool Dialog_Numeric_ShowAndGetDate(tm &date, const char *strHeading); + static bool Dialog_Numeric_ShowAndGetIPAddress(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading); + static bool Dialog_Numeric_ShowAndGetNumber(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs); + static bool Dialog_Numeric_ShowAndGetSeconds(char &timeString, unsigned int iMaxStringSize, const char *strHeading); + + static bool Dialog_FileBrowser_ShowAndGetFile(const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList); + + static void Dialog_OK_ShowAndGetInputSingleText(const char *heading, const char *text); + static void Dialog_OK_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2); + + static bool Dialog_YesNo_ShowAndGetInputSingleText(const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel); + static bool Dialog_YesNo_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel); + static bool Dialog_YesNo_ShowAndGetInputLineButtonText(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel); + + static void Dialog_TextViewer(const char *heading, const char *text); + static int Dialog_Select(const char *heading, const char *entries[], unsigned int size, int selected); + +private: + CB_GUILib *m_callbacks; + CAddon *m_addon; +}; + +class CGUIAddonWindow : public CGUIMediaWindow +{ +friend class CAddonCallbacksGUI; + +public: + CGUIAddonWindow(int id, const std::string& strXML, CAddon* addon); + virtual ~CGUIAddonWindow(void); + + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(bool forceLoad = false); + virtual void FreeResources(bool forceUnLoad = false); + virtual void Render(); + void WaitForActionEvent(unsigned int timeout); + void PulseActionEvent(); + void AddItem(CFileItemPtr fileItem, int itemPosition); + void RemoveItem(int itemPosition); + void ClearList(); + CFileItemPtr GetListItem(int position); + int GetListSize(); + int GetCurrentListPosition(); + void SetCurrentListPosition(int item); + virtual bool OnClick(int iItem); + +protected: + virtual void Update(); + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons); + void SetupShares(); + + bool (*CBOnInit)(GUIHANDLE cbhdl); + bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnAction)(GUIHANDLE cbhdl, int); + + GUIHANDLE m_clientHandle; + const int m_iWindowId; + int m_iOldWindowId; + bool m_bModal; + bool m_bIsDialog; + +private: + CEvent m_actionEvent; + CAddon *m_addon; + std::string m_mediaDir; +}; + +class CGUIAddonWindowDialog : public CGUIAddonWindow +{ +public: + CGUIAddonWindowDialog(int id, const std::string& strXML, CAddon* addon); + virtual ~CGUIAddonWindowDialog(void); + + void Show(bool show = true); + virtual bool OnMessage(CGUIMessage &message); + virtual bool IsDialogRunning() const { return m_bRunning; } + virtual bool IsDialog() const { return true;}; + virtual bool IsModalDialog() const { return true; }; + virtual bool IsMediaWindow() const { return false; }; + + void Show_Internal(bool show = true); + +private: + bool m_bRunning; +}; + +class CGUIAddonRenderingControl : public IRenderingCallback +{ +friend class CAddonCallbacksGUI; +public: + CGUIAddonRenderingControl(CGUIRenderingControl *pControl); + virtual ~CGUIAddonRenderingControl() {} + virtual bool Create(int x, int y, int w, int h, void *device); + virtual void Render(); + virtual void Stop(); + virtual bool IsDirty(); + virtual void Delete(); +protected: + bool (*CBCreate) (GUIHANDLE cbhdl, int x, int y, int w, int h, void *device); + void (*CBRender)(GUIHANDLE cbhdl); + void (*CBStop)(GUIHANDLE cbhdl); + bool (*CBDirty)(GUIHANDLE cbhdl); + + GUIHANDLE m_clientHandle; + CGUIRenderingControl *m_pControl; + int m_refCount; +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksPVR.cpp b/xbmc/addons/AddonCallbacksPVR.cpp new file mode 100644 index 0000000..1d769e1 --- /dev/null +++ b/xbmc/addons/AddonCallbacksPVR.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Application.h" +#include "AddonCallbacksPVR.h" +#include "settings/AdvancedSettings.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "dialogs/GUIDialogKaiToast.h" + +#include "epg/EpgContainer.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/channels/PVRChannelGroupInternal.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimerInfoTag.h" + +using namespace PVR; +using namespace EPG; + +namespace ADDON +{ + +CAddonCallbacksPVR::CAddonCallbacksPVR(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_PVRLib; + + /* write XBMC PVR specific add-on function addresses to callback table */ + m_callbacks->TransferEpgEntry = PVRTransferEpgEntry; + m_callbacks->TransferChannelEntry = PVRTransferChannelEntry; + m_callbacks->TransferTimerEntry = PVRTransferTimerEntry; + m_callbacks->TransferRecordingEntry = PVRTransferRecordingEntry; + m_callbacks->AddMenuHook = PVRAddMenuHook; + m_callbacks->Recording = PVRRecording; + m_callbacks->TriggerChannelUpdate = PVRTriggerChannelUpdate; + m_callbacks->TriggerChannelGroupsUpdate = PVRTriggerChannelGroupsUpdate; + m_callbacks->TriggerTimerUpdate = PVRTriggerTimerUpdate; + m_callbacks->TriggerRecordingUpdate = PVRTriggerRecordingUpdate; + m_callbacks->TriggerEpgUpdate = PVRTriggerEpgUpdate; + m_callbacks->FreeDemuxPacket = PVRFreeDemuxPacket; + m_callbacks->AllocateDemuxPacket = PVRAllocateDemuxPacket; + m_callbacks->TransferChannelGroup = PVRTransferChannelGroup; + m_callbacks->TransferChannelGroupMember = PVRTransferChannelGroupMember; +} + +CAddonCallbacksPVR::~CAddonCallbacksPVR() +{ + /* delete the callback table */ + delete m_callbacks; +} + +CPVRClient *CAddonCallbacksPVR::GetPVRClient(void *addonData) +{ + CAddonCallbacks *addon = static_cast(addonData); + if (!addon || !addon->GetHelperPVR()) + { + CLog::Log(LOGERROR, "PVR - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + return dynamic_cast(addon->GetHelperPVR()->m_addon); +} + +void CAddonCallbacksPVR::PVRTransferChannelGroup(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelGroups *xbmcGroups = static_cast(handle->dataAddress); + if (!group || !xbmcGroups) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + if (strlen(group->strGroupName) == 0) + { + CLog::Log(LOGERROR, "PVR - %s - empty group name", __FUNCTION__); + return; + } + + /* transfer this entry to the groups container */ + CPVRChannelGroup transferGroup(*group); + xbmcGroups->UpdateFromClient(transferGroup); +} + +void CAddonCallbacksPVR::PVRTransferChannelGroupMember(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRChannelGroup *group = static_cast(handle->dataAddress); + if (!member || !client || !group) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(member->iChannelUniqueId, client->GetID()); + if (!channel) + { + CLog::Log(LOGERROR, "PVR - %s - cannot find group '%s' or channel '%d'", __FUNCTION__, member->strGroupName, member->iChannelUniqueId); + } + else if (group->IsRadio() == channel->IsRadio()) + { + /* transfer this entry to the group */ + group->AddToGroup(channel, member->iChannelNumber); + } +} + +void CAddonCallbacksPVR::PVRTransferEpgEntry(void *addonData, const ADDON_HANDLE handle, const EPG_TAG *epgentry) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CEpg *xbmcEpg = static_cast(handle->dataAddress); + if (!xbmcEpg) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the epg */ + xbmcEpg->UpdateEntry(epgentry, handle->dataIdentifier == 1 /* update db */); +} + +void CAddonCallbacksPVR::PVRTransferChannelEntry(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL *channel) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRChannelGroupInternal *xbmcChannels = static_cast(handle->dataAddress); + if (!channel || !client || !xbmcChannels) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the internal channels group */ + CPVRChannelPtr transferChannel(new CPVRChannel(*channel, client->GetID())); + xbmcChannels->UpdateFromClient(transferChannel); +} + +void CAddonCallbacksPVR::PVRTransferRecordingEntry(void *addonData, const ADDON_HANDLE handle, const PVR_RECORDING *recording) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRRecordings *xbmcRecordings = static_cast(handle->dataAddress); + if (!recording || !client || !xbmcRecordings) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the recordings container */ + CPVRRecordingPtr transferRecording(new CPVRRecording(*recording, client->GetID())); + xbmcRecordings->UpdateFromClient(transferRecording); +} + +void CAddonCallbacksPVR::PVRTransferTimerEntry(void *addonData, const ADDON_HANDLE handle, const PVR_TIMER *timer) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRTimers *xbmcTimers = static_cast(handle->dataAddress); + if (!timer || !client || !xbmcTimers) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(timer->iClientChannelUid, client->GetID()); + if (!channel) + { + CLog::Log(LOGERROR, "PVR - %s - cannot find channel %d on client %d", __FUNCTION__, timer->iClientChannelUid, client->GetID()); + return; + } + + /* transfer this entry to the timers container */ + CPVRTimerInfoTag transferTimer(*timer, channel, client->GetID()); + xbmcTimers->UpdateFromClient(transferTimer); +} + +void CAddonCallbacksPVR::PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook) +{ + CPVRClient *client = GetPVRClient(addonData); + if (!hook || !client) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + PVR_MENUHOOKS *hooks = client->GetMenuHooks(); + if (hooks) + { + PVR_MENUHOOK hookInt; + hookInt.iHookId = hook->iHookId; + hookInt.iLocalizedStringId = hook->iLocalizedStringId; + hookInt.category = hook->category; + + /* add this new hook */ + hooks->push_back(hookInt); + } +} + +void CAddonCallbacksPVR::PVRRecording(void *addonData, const char *strName, const char *strFileName, bool bOnOff) +{ + CPVRClient *client = GetPVRClient(addonData); + if (!client || !strFileName) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + std::string strLine1; + if (bOnOff) + strLine1 = StringUtils::Format(g_localizeStrings.Get(19197).c_str(), client->Name().c_str()); + else + strLine1 = StringUtils::Format(g_localizeStrings.Get(19198).c_str(), client->Name().c_str()); + + std::string strLine2; + if (strName) + strLine2 = strName; + else if (strFileName) + strLine2 = strFileName; + + /* display a notification for 5 seconds */ + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strLine1, strLine2, 5000, false); + + CLog::Log(LOGDEBUG, "PVR - %s - recording %s on client '%s'. name='%s' filename='%s'", + __FUNCTION__, bOnOff ? "started" : "finished", client->Name().c_str(), strName, strFileName); +} + +void CAddonCallbacksPVR::PVRTriggerChannelUpdate(void *addonData) +{ + /* update the channels table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerChannelsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerTimerUpdate(void *addonData) +{ + /* update the timers table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerTimersUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerRecordingUpdate(void *addonData) +{ + /* update the recordings table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerRecordingsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerChannelGroupsUpdate(void *addonData) +{ + /* update all channel groups in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerChannelGroupsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerEpgUpdate(void *addonData, unsigned int iChannelUid) +{ + // get the client + CPVRClient *client = GetPVRClient(addonData); + if (!client) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + g_EpgContainer.UpdateRequest(client->GetID(), iChannelUid); +} + +void CAddonCallbacksPVR::PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket) +{ + CDVDDemuxUtils::FreeDemuxPacket(pPacket); +} + +DemuxPacket* CAddonCallbacksPVR::PVRAllocateDemuxPacket(void *addonData, int iDataSize) +{ + return CDVDDemuxUtils::AllocateDemuxPacket(iDataSize); +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksPVR.h b/xbmc/addons/AddonCallbacksPVR.h new file mode 100644 index 0000000..3fa48ec --- /dev/null +++ b/xbmc/addons/AddonCallbacksPVR.h @@ -0,0 +1,166 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "AddonCallbacks.h" +#include "include/xbmc_pvr_types.h" + +namespace PVR +{ + class CPVRClient; +} + +namespace ADDON +{ + +/*! + * Callbacks for a PVR add-on to XBMC. + * + * Also translates the addon's C structures to XBMC's C++ structures. + */ +class CAddonCallbacksPVR +{ +public: + CAddonCallbacksPVR(CAddon* addon); + ~CAddonCallbacksPVR(void); + + /*! + * @return The callback table. + */ + CB_PVRLib *GetCallbacks() { return m_callbacks; } + + /*! + * @brief Transfer a channel group from the add-on to XBMC. The group will be created if it doesn't exist. + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the channel groups list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferChannelGroup(void* addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP* entry); + + /*! + * @brief Transfer a channel group member entry from the add-on to XBMC. The channel will be added to the group if the group can be found. + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the channel group members list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferChannelGroupMember(void* addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER* entry); + + /*! + * @brief Transfer an EPG tag from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the EPG data + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferEpgEntry(void* addonData, const ADDON_HANDLE handle, const EPG_TAG* entry); + + /*! + * @brief Transfer a channel entry from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the channel list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferChannelEntry(void* addonData, const ADDON_HANDLE handle, const PVR_CHANNEL* entry); + + /*! + * @brief Transfer a timer entry from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the timers list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferTimerEntry(void* addonData, const ADDON_HANDLE handle, const PVR_TIMER* entry); + + /*! + * @brief Transfer a recording entry from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the recordings list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferRecordingEntry(void* addonData, const ADDON_HANDLE handle, const PVR_RECORDING* entry); + + /*! + * @brief Add or replace a menu hook for the context menu for this add-on + * @param addonData A pointer to the add-on. + * @param hook The hook to add. + */ + static void PVRAddMenuHook(void* addonData, PVR_MENUHOOK* hook); + + /*! + * @brief Display a notification in XBMC that a recording started or stopped on the server + * @param addonData A pointer to the add-on. + * @param strName The name of the recording to display + * @param strFileName The filename of the recording + * @param bOnOff True when recording started, false when it stopped + */ + static void PVRRecording(void* addonData, const char* strName, const char* strFileName, bool bOnOff); + + /*! + * @brief Request XBMC to update it's list of channels + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerChannelUpdate(void* addonData); + + /*! + * @brief Request XBMC to update it's list of timers + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerTimerUpdate(void* addonData); + + /*! + * @brief Request XBMC to update it's list of recordings + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerRecordingUpdate(void* addonData); + + /*! + * @brief Request XBMC to update it's list of channel groups + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerChannelGroupsUpdate(void* addonData); + + /*! + * @brief Schedule an EPG update for the given channel channel + * @param addonData A pointer to the add-on + * @param iChannelUid The unique id of the channel for this add-on + */ + static void PVRTriggerEpgUpdate(void* addonData, unsigned int iChannelUid); + + /*! + * @brief Free a packet that was allocated with AllocateDemuxPacket + * @param addonData A pointer to the add-on. + * @param pPacket The packet to free. + */ + static void PVRFreeDemuxPacket(void* addonData, DemuxPacket* pPacket); + + /*! + * @brief Allocate a demux packet. Free with FreeDemuxPacket + * @param addonData A pointer to the add-on. + * @param iDataSize The size of the data that will go into the packet + * @return The allocated packet. + */ + static DemuxPacket* PVRAllocateDemuxPacket(void* addonData, int iDataSize = 0); + +private: + static PVR::CPVRClient* GetPVRClient(void* addonData); + + CB_PVRLib *m_callbacks; /*!< callback addresses */ + CAddon *m_addon; /*!< the addon */ +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp new file mode 100644 index 0000000..69b47ea --- /dev/null +++ b/xbmc/addons/AddonDatabase.cpp @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "AddonDatabase.h" +#include "addons/AddonManager.h" +#include "utils/log.h" +#include "utils/Variant.h" +#include "utils/StringUtils.h" +#include "XBDateTime.h" +#include "dbwrappers/dataset.h" +#include "addons/ContextItemAddon.h" + +using namespace ADDON; +using namespace std; + +CAddonDatabase::CAddonDatabase() +{ +} + +CAddonDatabase::~CAddonDatabase() +{ +} + +bool CAddonDatabase::Open() +{ + return CDatabase::Open(); +} + +void CAddonDatabase::CreateTables() +{ + CLog::Log(LOGINFO, "create addon table"); + m_pDS->exec("CREATE TABLE addon (id integer primary key, type text," + "name text, summary text, description text, stars integer," + "path text, addonID text, icon text, version text, " + "changelog text, fanart text, author text, disclaimer text," + "minversion text)\n"); + + CLog::Log(LOGINFO, "create addonextra table"); + m_pDS->exec("CREATE TABLE addonextra (id integer, key text, value text)\n"); + + CLog::Log(LOGINFO, "create dependencies table"); + m_pDS->exec("CREATE TABLE dependencies (id integer, addon text, version text, optional boolean)\n"); + + CLog::Log(LOGINFO, "create repo table"); + m_pDS->exec("CREATE TABLE repo (id integer primary key, addonID text," + "checksum text, lastcheck text, version text)\n"); + + CLog::Log(LOGINFO, "create addonlinkrepo table"); + m_pDS->exec("CREATE TABLE addonlinkrepo (idRepo integer, idAddon integer)\n"); + + CLog::Log(LOGINFO, "create disabled table"); + m_pDS->exec("CREATE TABLE disabled (id integer primary key, addonID text)\n"); + + CLog::Log(LOGINFO, "create broken table"); + m_pDS->exec("CREATE TABLE broken (id integer primary key, addonID text, reason text)\n"); + + CLog::Log(LOGINFO, "create blacklist table"); + m_pDS->exec("CREATE TABLE blacklist (id integer primary key, addonID text, version text)\n"); + + CLog::Log(LOGINFO, "create package table"); + m_pDS->exec("CREATE TABLE package (id integer primary key, addonID text, filename text, hash text)\n"); +} + +void CAddonDatabase::CreateAnalytics() +{ + CLog::Log(LOGINFO, "%s creating indicies", __FUNCTION__); + m_pDS->exec("CREATE INDEX idxAddon ON addon(addonID)"); + m_pDS->exec("CREATE INDEX idxAddonExtra ON addonextra(id)"); + m_pDS->exec("CREATE INDEX idxDependencies ON dependencies(id)"); + m_pDS->exec("CREATE UNIQUE INDEX ix_addonlinkrepo_1 ON addonlinkrepo ( idAddon, idRepo )\n"); + m_pDS->exec("CREATE UNIQUE INDEX ix_addonlinkrepo_2 ON addonlinkrepo ( idRepo, idAddon )\n"); + m_pDS->exec("CREATE UNIQUE INDEX idxDisabled ON disabled(addonID)"); + m_pDS->exec("CREATE UNIQUE INDEX idxBroken ON broken(addonID)"); + m_pDS->exec("CREATE UNIQUE INDEX idxBlack ON blacklist(addonID)"); + m_pDS->exec("CREATE UNIQUE INDEX idxPackage ON package(filename)"); +} + +void CAddonDatabase::UpdateTables(int version) +{ + if (version < 16) + { + m_pDS->exec("CREATE TABLE package (id integer primary key, addonID text, filename text, hash text)\n"); + } + if (version < 17) + { + m_pDS->exec("DELETE FROM repo"); + m_pDS->exec("ALTER TABLE repo ADD version text"); + } +} + +int CAddonDatabase::AddAddon(const AddonPtr& addon, + int idRepo) +{ + try + { + if (NULL == m_pDB.get()) return -1; + if (NULL == m_pDS.get()) return -1; + + std::string sql = PrepareSQL("insert into addon (id, type, name, summary," + "description, stars, path, icon, changelog, " + "fanart, addonID, version, author, disclaimer, minversion)" + " values(NULL, '%s', '%s', '%s', '%s', %i," + "'%s', '%s', '%s', '%s', '%s','%s','%s','%s','%s')", + TranslateType(addon->Type(),false).c_str(), + addon->Name().c_str(), addon->Summary().c_str(), + addon->Description().c_str(),addon->Stars(), + addon->Path().c_str(), addon->Props().icon.c_str(), + addon->ChangeLog().c_str(),addon->FanArt().c_str(), + addon->ID().c_str(), addon->Version().asString().c_str(), + addon->Author().c_str(),addon->Disclaimer().c_str(), + addon->MinVersion().asString().c_str()); + m_pDS->exec(sql.c_str()); + int idAddon = (int)m_pDS->lastinsertid(); + + sql = PrepareSQL("insert into addonlinkrepo (idRepo, idAddon) values (%i,%i)",idRepo,idAddon); + m_pDS->exec(sql.c_str()); + + const InfoMap &info = addon->ExtraInfo(); + for (InfoMap::const_iterator i = info.begin(); i != info.end(); ++i) + { + sql = PrepareSQL("insert into addonextra(id, key, value) values (%i, '%s', '%s')", idAddon, i->first.c_str(), i->second.c_str()); + m_pDS->exec(sql.c_str()); + } + const ADDONDEPS &deps = addon->GetDeps(); + for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) + { + sql = PrepareSQL("insert into dependencies(id, addon, version, optional) values (%i, '%s', '%s', %i)", idAddon, i->first.c_str(), i->second.first.asString().c_str(), i->second.second ? 1 : 0); + m_pDS->exec(sql.c_str()); + } + return idAddon; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addon->Name().c_str()); + } + return -1; +} + +AddonVersion CAddonDatabase::GetAddonVersion(const std::string &id) +{ + AddonVersion maxversion("0.0.0"); + try + { + if (NULL == m_pDB.get()) return maxversion; + if (NULL == m_pDS2.get()) return maxversion; + + // there may be multiple addons with this id (eg from different repositories) in the database, + // so we want to retrieve the latest version. Order by version won't work as the database + // won't know that 1.10 > 1.2, so grab them all and order outside + std::string sql = PrepareSQL("select version from addon where addonID='%s'",id.c_str()); + m_pDS2->query(sql.c_str()); + + if (m_pDS2->eof()) + return maxversion; + + while (!m_pDS2->eof()) + { + AddonVersion version(m_pDS2->fv(0).get_asString()); + if (version > maxversion) + maxversion = version; + m_pDS2->next(); + } + return maxversion; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str()); + } + return maxversion; +} + +bool CAddonDatabase::GetAddon(const std::string& id, AddonPtr& addon) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + // there may be multiple addons with this id (eg from different repositories) in the database, + // so we want to retrieve the latest version. Order by version won't work as the database + // won't know that 1.10 > 1.2, so grab them all and order outside + std::string sql = PrepareSQL("select id,version from addon where addonID='%s'",id.c_str()); + m_pDS2->query(sql.c_str()); + + if (m_pDS2->eof()) + return false; + + AddonVersion maxversion("0.0.0"); + int maxid = 0; + while (!m_pDS2->eof()) + { + AddonVersion version(m_pDS2->fv(1).get_asString()); + if (version > maxversion) + { + maxid = m_pDS2->fv(0).get_asInt(); + maxversion = version; + } + m_pDS2->next(); + } + return GetAddon(maxid,addon); + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str()); + } + addon.reset(); + return false; +} + +bool CAddonDatabase::GetRepoForAddon(const std::string& addonID, std::string& repo) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + std::string sql = PrepareSQL("select repo.addonID from repo join addonlinkrepo on repo.id=addonlinkrepo.idRepo join addon on addonlinkrepo.idAddon=addon.id where addon.addonID like '%s'", addonID.c_str()); + m_pDS2->query(sql.c_str()); + if (!m_pDS2->eof()) + { + repo = m_pDS2->fv(0).get_asString(); + m_pDS2->close(); + return true; + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed for addon %s", __FUNCTION__, addonID.c_str()); + } + return false; +} + +bool CAddonDatabase::GetAddon(int id, AddonPtr &addon) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + std::string sql = "SELECT addon.*," + " broken.reason," + " addonextra.key, addonextra.value," + " dependencies.addon, dependencies.version, dependencies.optional" + " FROM addon" + " LEFT JOIN broken" + " ON broken.addonID = addon.addonID" + " LEFT JOIN addonextra" + " ON addonextra.id = addon.id" + " LEFT JOIN dependencies" + " ON dependencies.id = addon.id"; + + sql += PrepareSQL(" WHERE addon.id=%i", id); + + m_pDS2->query(sql.c_str()); + if (!m_pDS2->eof()) + { + const dbiplus::query_data &data = m_pDS2->get_result_set().records; + const dbiplus::sql_record* const record = data[0]; + AddonProps props(record->at(addon_addonID).get_asString(), + TranslateType(record->at(addon_type).get_asString()), + record->at(addon_version).get_asString(), + record->at(addon_minversion).get_asString()); + props.name = record->at(addon_name).get_asString(); + props.summary = record->at(addon_summary).get_asString(); + props.description = record->at(addon_description).get_asString(); + props.changelog = record->at(addon_changelog).get_asString(); + props.path = record->at(addon_path).get_asString(); + props.icon = record->at(addon_icon).get_asString(); + props.fanart = record->at(addon_fanart).get_asString(); + props.author = record->at(addon_author).get_asString(); + props.disclaimer = record->at(addon_disclaimer).get_asString(); + props.broken = record->at(broken_reason).get_asString(); + + /* while this is a cartesion join and we'll typically get multiple rows, we rely on the fact that + extrainfo and dependencies are maps, so insert() will insert the first instance only */ + for (dbiplus::query_data::const_iterator i = data.begin(); i != data.end(); ++i) + { + const dbiplus::sql_record* const record = *i; + if (!record->at(addonextra_key).get_asString().empty()) + props.extrainfo.insert(make_pair(record->at(addonextra_key).get_asString(), record->at(addonextra_value).get_asString())); + if (!m_pDS2->fv(dependencies_addon).get_asString().empty()) + props.dependencies.insert(make_pair(record->at(dependencies_addon).get_asString(), make_pair(AddonVersion(record->at(dependencies_version).get_asString()), record->at(dependencies_optional).get_asBool()))); + } + + addon = CAddonMgr::AddonFromProps(props); + return NULL != addon.get(); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id); + } + addon.reset(); + return false; +} + +bool CAddonDatabase::GetAddons(VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + std::string sql = PrepareSQL("select distinct addonID from addon"); + m_pDS->query(sql.c_str()); + while (!m_pDS->eof()) + { + AddonPtr addon; + if (GetAddon(m_pDS->fv(0).get_asString(),addon)) + addons.push_back(addon); + m_pDS->next(); + } + m_pDS->close(); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + +void CAddonDatabase::DeleteRepository(const std::string& id) +{ + try + { + if (NULL == m_pDB.get()) return; + if (NULL == m_pDS.get()) return; + + std::string sql = PrepareSQL("select id from repo where addonID='%s'",id.c_str()); + m_pDS->query(sql.c_str()); + if (!m_pDS->eof()) + DeleteRepository(m_pDS->fv(0).get_asInt()); + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } +} + +void CAddonDatabase::DeleteRepository(int idRepo) +{ + try + { + if (NULL == m_pDB.get()) return; + if (NULL == m_pDS.get()) return; + + std::string sql = PrepareSQL("delete from repo where id=%i",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from addon where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from addonextra where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from dependencies where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from addonlinkrepo where idRepo=%i",idRepo); + m_pDS->exec(sql.c_str()); + + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo %i", __FUNCTION__, idRepo); + } +} + +int CAddonDatabase::AddRepository(const std::string& id, const VECADDONS& addons, const std::string& checksum, const AddonVersion& version) +{ + try + { + if (NULL == m_pDB.get()) return -1; + if (NULL == m_pDS.get()) return -1; + + std::string sql; + int idRepo = GetRepoChecksum(id,sql); + if (idRepo > -1) + DeleteRepository(idRepo); + + BeginTransaction(); + + CDateTime time = CDateTime::GetCurrentDateTime(); + sql = PrepareSQL("insert into repo (id,addonID,checksum,lastcheck,version) values (NULL,'%s','%s','%s','%s')", + id.c_str(), checksum.c_str(), time.GetAsDBDateTime().c_str(), version.asString().c_str()); + m_pDS->exec(sql.c_str()); + idRepo = (int)m_pDS->lastinsertid(); + for (unsigned int i=0;iquery(strSQL.c_str()); + if (!m_pDS->eof()) + { + checksum = m_pDS->fv("checksum").get_asString(); + return m_pDS->fv("id").get_asInt(); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } + checksum.clear(); + return -1; +} + +CDateTime CAddonDatabase::GetRepoTimestamp(const std::string& id) +{ + CDateTime date; + try + { + if (NULL == m_pDB.get()) return date; + if (NULL == m_pDS.get()) return date; + + std::string strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str()); + m_pDS->query(strSQL.c_str()); + if (!m_pDS->eof()) + { + date.SetFromDBDateTime(m_pDS->fv("lastcheck").get_asString()); + return date; + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } + return date; +} + + +AddonVersion CAddonDatabase::GetRepoVersion(const std::string& id) +{ + AddonVersion version("0.0.0"); + try + { + if (NULL == m_pDB.get()) return version; + if (NULL == m_pDS2.get()) return version; + + std::string strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str()); + m_pDS->query(strSQL.c_str()); + if (!m_pDS->eof()) + { + return AddonVersion(m_pDS->fv("version").get_asString()); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str()); + } + return version; +} + +bool CAddonDatabase::SetRepoTimestamp(const std::string& id, const std::string& time, const ADDON::AddonVersion& version) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("UPDATE repo SET lastcheck='%s', version='%s' WHERE addonID='%s'", + time.c_str(), version.asString().c_str(), id.c_str()); + m_pDS->exec(sql.c_str()); + + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } + return false; +} + +bool CAddonDatabase::GetRepository(int id, VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL = PrepareSQL("select * from addonlinkrepo where idRepo=%i",id); + m_pDS->query(strSQL.c_str()); + while (!m_pDS->eof()) + { + AddonPtr addon; + if (GetAddon(m_pDS->fv("idAddon").get_asInt(),addon)) + addons.push_back(addon); + m_pDS->next(); + } + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo %i", __FUNCTION__, id); + } + return false; +} + +bool CAddonDatabase::GetRepository(const std::string& id, VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL = PrepareSQL("select id from repo where addonID='%s'",id.c_str()); + m_pDS->query(strSQL.c_str()); + if (!m_pDS->eof()) + return GetRepository(m_pDS->fv(0).get_asInt(),addons); + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo %s", __FUNCTION__, id.c_str()); + } + return false; +} + +bool CAddonDatabase::Search(const std::string& search, VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL; + strSQL=PrepareSQL("SELECT addonID FROM addon WHERE name LIKE '%%%s%%' OR summary LIKE '%%%s%%' OR description LIKE '%%%s%%'", search.c_str(), search.c_str(), search.c_str()); + CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str()); + + if (!m_pDS->query(strSQL.c_str())) return false; + if (m_pDS->num_rows() == 0) return false; + + while (!m_pDS->eof()) + { + AddonPtr addon; + GetAddon(m_pDS->fv(0).get_asString(),addon); + if (addon->Type() >= ADDON_UNKNOWN+1 && addon->Type() < ADDON_SCRAPER_LIBRARY) + addons.push_back(addon); + m_pDS->next(); + } + m_pDS->close(); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + +void CAddonDatabase::SetPropertiesFromAddon(const AddonPtr& addon, + CFileItemPtr& pItem) +{ + pItem->SetProperty("Addon.ID", addon->ID()); + pItem->SetProperty("Addon.Type", TranslateType(addon->Type(),true)); + pItem->SetProperty("Addon.intType", TranslateType(addon->Type())); + pItem->SetProperty("Addon.Name", addon->Name()); + pItem->SetProperty("Addon.Version", addon->Version().asString()); + pItem->SetProperty("Addon.Summary", addon->Summary()); + pItem->SetProperty("Addon.Description", addon->Description()); + pItem->SetProperty("Addon.Creator", addon->Author()); + pItem->SetProperty("Addon.Disclaimer", addon->Disclaimer()); + pItem->SetProperty("Addon.Rating", addon->Stars()); + std::string starrating = StringUtils::Format("rating%d.png", addon->Stars()); + pItem->SetProperty("Addon.StarRating",starrating); + pItem->SetProperty("Addon.Path", addon->Path()); + if (addon->Props().broken == "DEPSNOTMET") + pItem->SetProperty("Addon.Broken", g_localizeStrings.Get(24044)); + else + pItem->SetProperty("Addon.Broken", addon->Props().broken); + std::map::iterator it = + addon->Props().extrainfo.find("language"); + if (it != addon->Props().extrainfo.end()) + pItem->SetProperty("Addon.Language", it->second); +} + +bool CAddonDatabase::DisableAddon(const std::string &addonID, bool disable /* = true */) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + if (disable) + { + if (!IsAddonDisabled(addonID)) // Enabled + { + std::string sql = PrepareSQL("insert into disabled(id, addonID) values(NULL, '%s')", addonID.c_str()); + m_pDS->exec(sql); + + // If the addon is a special, call the disabled handler + AddonPtr addon; + if ((CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_CONTEXT_ITEM, false)) && addon) + addon->OnDisabled(); + + return true; + } + return false; // already disabled or failed query + } + else + { + bool disabled = IsAddonDisabled(addonID); //we need to know if service addon is running + std::string sql = PrepareSQL("delete from disabled where addonID='%s'", addonID.c_str()); + m_pDS->exec(sql); + + if (disabled) + { + // If the addon is a special, call the enabled handler + AddonPtr addon; + if ((CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_CONTEXT_ITEM, false)) && addon) + addon->OnEnabled(); + } + } + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addonID.c_str()); + } + return false; +} + +bool CAddonDatabase::BreakAddon(const std::string &addonID, const std::string& reason) +{ + if (reason.empty()) + return ExecuteQuery(PrepareSQL("DELETE FROM broken WHERE addonID='%s'", addonID.c_str())); + else + return ExecuteQuery(PrepareSQL("REPLACE INTO broken(addonID, reason) VALUES('%s', '%s')", + addonID.c_str(), reason.c_str())); +} + +bool CAddonDatabase::HasAddon(const std::string &addonID) +{ + std::string strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str()); + std::string strHasAddon = GetSingleValue("addon", "id", strWhereClause); + + return !strHasAddon.empty(); +} + +bool CAddonDatabase::IsAddonDisabled(const std::string &addonID) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("select id from disabled where addonID='%s'", addonID.c_str()); + m_pDS->query(sql.c_str()); + bool ret = !m_pDS->eof(); // in the disabled table -> disabled + m_pDS->close(); + return ret; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, addonID.c_str()); + } + return false; +} + +bool CAddonDatabase::IsSystemPVRAddonEnabled(const std::string &addonID) +{ + std::string strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str()); + std::string strEnabled = GetSingleValue("pvrenabled", "id", strWhereClause); + + return !strEnabled.empty(); +} + +std::string CAddonDatabase::IsAddonBroken(const std::string &addonID) +{ + return GetSingleValue(PrepareSQL("SELECT reason FROM broken WHERE addonID='%s'", addonID.c_str())); +} + +bool CAddonDatabase::HasDisabledAddons() +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + m_pDS->query("select count(id) from disabled"); + bool ret = !m_pDS->eof() && m_pDS->fv(0).get_asInt() > 0; // have rows -> have disabled addons + m_pDS->close(); + return ret; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + +bool CAddonDatabase::BlacklistAddon(const std::string& addonID, + const std::string& version) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("insert into blacklist(id, addonID, version) values(NULL, '%s', '%s')", addonID.c_str(),version.c_str()); + m_pDS->exec(sql); + + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s' for version '%s'", __FUNCTION__, addonID.c_str(),version.c_str()); + } + return false; +} + +bool CAddonDatabase::IsAddonBlacklisted(const std::string& addonID, + const std::string& version) +{ + std::string where = PrepareSQL("addonID='%s' and version='%s'",addonID.c_str(),version.c_str()); + return !GetSingleValue("blacklist","addonID",where).empty(); +} + +bool CAddonDatabase::RemoveAddonFromBlacklist(const std::string& addonID, + const std::string& version) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("delete from blacklist where addonID='%s' and version='%s'",addonID.c_str(),version.c_str()); + m_pDS->exec(sql); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s' for version '%s'", __FUNCTION__, addonID.c_str(),version.c_str()); + } + return false; +} + +bool CAddonDatabase::AddPackage(const std::string& addonID, + const std::string& packageFileName, + const std::string& hash) +{ + std::string sql = PrepareSQL("insert into package(id, addonID, filename, hash)" + "values(NULL, '%s', '%s', '%s')", + addonID.c_str(), packageFileName.c_str(), hash.c_str()); + return ExecuteQuery(sql); +} + +bool CAddonDatabase::GetPackageHash(const std::string& addonID, + const std::string& packageFileName, + std::string& hash) +{ + std::string where = PrepareSQL("addonID='%s' and filename='%s'", + addonID.c_str(), packageFileName.c_str()); + hash = GetSingleValue("package", "hash", where); + return !hash.empty(); +} + +bool CAddonDatabase::RemovePackage(const std::string& packageFileName) +{ + std::string sql = PrepareSQL("delete from package where filename='%s'", packageFileName.c_str()); + return ExecuteQuery(sql); +} + diff --git a/xbmc/addons/AddonDatabase.h b/xbmc/addons/AddonDatabase.h new file mode 100644 index 0000000..b32f2a4 --- /dev/null +++ b/xbmc/addons/AddonDatabase.h @@ -0,0 +1,175 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "dbwrappers/Database.h" +#include "addons/Addon.h" +#include "FileItem.h" +#include + +class CAddonDatabase : public CDatabase +{ +public: + CAddonDatabase(); + virtual ~CAddonDatabase(); + virtual bool Open(); + + int AddAddon(const ADDON::AddonPtr& item, int idRepo); + bool GetAddon(const std::string& addonID, ADDON::AddonPtr& addon); + bool GetAddons(ADDON::VECADDONS& addons); + + /*! \brief grab the (largest) add-on version for an add-on */ + ADDON::AddonVersion GetAddonVersion(const std::string &id); + + /*! \brief Grab the repository from which a given addon came + \param addonID - the id of the addon in question + \param repo [out] - the id of the repository + \return true if a repo was found, false otherwise. + */ + bool GetRepoForAddon(const std::string& addonID, std::string& repo); + int AddRepository(const std::string& id, const ADDON::VECADDONS& addons, const std::string& checksum, const ADDON::AddonVersion& version); + void DeleteRepository(const std::string& id); + void DeleteRepository(int id); + int GetRepoChecksum(const std::string& id, std::string& checksum); + bool GetRepository(const std::string& id, ADDON::VECADDONS& addons); + bool GetRepository(int id, ADDON::VECADDONS& addons); + bool SetRepoTimestamp(const std::string& id, const std::string& timestamp, const ADDON::AddonVersion& version); + + /*! \brief Retrieve the time a repository was last checked + \param id id of the repo + \return last time the repo was checked, current time if not available + \sa SetRepoTimestamp */ + CDateTime GetRepoTimestamp(const std::string& id); + + ADDON::AddonVersion GetRepoVersion(const std::string& id); + + bool Search(const std::string& search, ADDON::VECADDONS& items); + static void SetPropertiesFromAddon(const ADDON::AddonPtr& addon, CFileItemPtr& item); + + /*! \brief Disable an addon. + Sets a flag that this addon has been disabled. If disabled, it is usually still available on disk. + \param addonID id of the addon to disable + \param disable whether to enable or disable. Defaults to true (disable) + \return true on success, false on failure + \sa IsAddonDisabled, HasDisabledAddons */ + bool DisableAddon(const std::string &addonID, bool disable = true); + + /*! \brief Checks if an addon is in the database. + \param addonID id of the addon to be checked + \return true if addon is in database, false if addon is not in database yet */ + bool HasAddon(const std::string &addonID); + + /*! \brief Check whether an addon has been disabled via DisableAddon. + \param addonID id of the addon to check + \return true if the addon is disabled, false otherwise + \sa DisableAddon, HasDisabledAddons */ + bool IsAddonDisabled(const std::string &addonID); + + /*! \brief Check whether we have disabled addons. + \return true if we have disabled addons, false otherwise + \sa DisableAddon, IsAddonDisabled */ + bool HasDisabledAddons(); + + /*! @deprecated only here to allow clean upgrades from earlier pvr versions + */ + bool IsSystemPVRAddonEnabled(const std::string &addonID); + + /*! \brief Mark an addon as broken + Sets a flag that this addon has been marked as broken in the repository. + \param addonID id of the addon to mark as broken + \param reason why it is broken - if non empty we take it as broken. Defaults to empty + \return true on success, false on failure + \sa IsAddonBroken */ + bool BreakAddon(const std::string &addonID, const std::string& reason=""); + + /*! \brief Check whether an addon has been marked as broken via BreakAddon. + \param addonID id of the addon to check + \return reason if the addon is broken, blank otherwise + \sa BreakAddon */ + std::string IsAddonBroken(const std::string &addonID); + + bool BlacklistAddon(const std::string& addonID, const std::string& version); + bool IsAddonBlacklisted(const std::string& addonID, const std::string& version); + bool RemoveAddonFromBlacklist(const std::string& addonID, + const std::string& version); + + /*! \brief Store an addon's package filename and that file's hash for future verification + \param addonID id of the addon we're adding a package for + \param packageFileName filename of the package + \param hash MD5 checksum of the package + \return Whether or not the info successfully made it into the DB. + \sa GetPackageHash, RemovePackage + */ + bool AddPackage(const std::string& addonID, + const std::string& packageFileName, + const std::string& hash); + /*! \brief Query the MD5 checksum of the given given addon's given package + \param addonID id of the addon we're who's package we're querying + \param packageFileName filename of the package + \param hash return the MD5 checksum of the package + \return Whether or not we found a hash for the given addon's given package + \sa AddPackage, RemovePackage + */ + bool GetPackageHash(const std::string& addonID, + const std::string& packageFileName, + std::string& hash); + /*! \brief Remove a package's info from the database + \param packageFileName filename of the package + \return Whether or not we succeeded in removing the package + \sa AddPackage, GetPackageHash + */ + bool RemovePackage(const std::string& packageFileName); +protected: + virtual void CreateTables(); + virtual void CreateAnalytics(); + virtual void UpdateTables(int version); + virtual int GetMinSchemaVersion() const { return 15; } + virtual int GetSchemaVersion() const { return 17; } + const char *GetBaseDBName() const { return "Addons"; } + + bool GetAddon(int id, ADDON::AddonPtr& addon); + + /* keep in sync with the select in GetAddon */ + enum _AddonFields + { + addon_id=0, + addon_type, + addon_name, + addon_summary, + addon_description, + addon_stars, + addon_path, + addon_addonID, + addon_icon, + addon_version, + addon_changelog, + addon_fanart, + addon_author, + addon_disclaimer, + addon_minversion, + broken_reason, + addonextra_key, + addonextra_value, + dependencies_addon, + dependencies_version, + dependencies_optional + } AddonFields; +}; + diff --git a/xbmc/addons/AddonDll.h b/xbmc/addons/AddonDll.h new file mode 100644 index 0000000..d5e4e9e --- /dev/null +++ b/xbmc/addons/AddonDll.h @@ -0,0 +1,572 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include +#include + +#include "Addon.h" +#include "DllAddon.h" +#include "AddonManager.h" +#include "AddonStatusHandler.h" +#include "AddonCallbacks.h" +#include "utils/URIUtils.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "filesystem/Directory.h" +#include "utils/log.h" +#include "interfaces/IAnnouncer.h" +#include "interfaces/AnnouncementManager.h" +#include "utils/XMLUtils.h" +#include "Util.h" + +namespace ADDON +{ + template + class CAddonDll : public CAddon, public ANNOUNCEMENT::IAnnouncer + { + public: + CAddonDll(const AddonProps &props); + CAddonDll(const cp_extension_t *ext); + CAddonDll(const CAddonDll &rhs); + virtual ~CAddonDll(); + virtual AddonPtr Clone() const; + virtual ADDON_STATUS GetStatus(); + + // addon settings + virtual void SaveSettings(); + virtual std::string GetSetting(const std::string& key); + + ADDON_STATUS Create(); + virtual void Stop(); + virtual bool CheckAPIVersion(void) { return true; } + void Destroy(); + + bool DllLoaded(void) const; + + void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data); + + protected: + void HandleException(std::exception &e, const char* context); + bool Initialized() { return m_initialized; } + virtual void BuildLibName(const cp_extension_t *ext = NULL) {} + virtual bool LoadSettings(); + TheStruct* m_pStruct; + TheProps* m_pInfo; + CAddonCallbacks* m_pHelpers; + bool m_bIsChild; + + private: + TheDll* m_pDll; + bool m_initialized; + bool LoadDll(); + bool m_needsavedsettings; + + virtual ADDON_STATUS TransferSettings(); + TiXmlElement MakeSetting(DllSetting& setting) const; + + static void AddOnStatusCallback(void *userData, const ADDON_STATUS status, const char* msg); + static bool AddOnGetSetting(void *userData, const char *settingName, void *settingValue); + static void AddOnOpenSettings(const char *url, bool bReload); + static void AddOnOpenOwnSettings(void *userData, bool bReload); + static const char* AddOnGetAddonDirectory(void *userData); + static const char* AddOnGetUserDirectory(void *userData); + }; + +template +CAddonDll::CAddonDll(const cp_extension_t *ext) + : CAddon(ext), + m_bIsChild(false) +{ + // if library attribute isn't present, look for a system-dependent one + if (ext && m_strLibName.empty()) + { +#if defined(TARGET_ANDROID) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_android"); +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_linux"); +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_windx"); +#elif defined(TARGET_DARWIN) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_osx"); +#endif + } + + m_pStruct = NULL; + m_initialized = false; + m_pDll = NULL; + m_pInfo = NULL; + m_pHelpers = NULL; + m_needsavedsettings = false; +} + +template +CAddonDll::CAddonDll(const AddonProps &props) + : CAddon(props), + m_bIsChild(false) +{ + m_pStruct = NULL; + m_initialized = false; + m_pDll = NULL; + m_pInfo = NULL; + m_pHelpers = NULL; + m_needsavedsettings = false; +} + +template +CAddonDll::CAddonDll(const CAddonDll &rhs) + : CAddon(rhs), + m_bIsChild(true) +{ + m_pStruct = rhs.m_pStruct; + m_initialized = rhs.m_initialized; + m_pDll = rhs.m_pDll; + m_pInfo = rhs.m_pInfo; + m_pHelpers = rhs.m_pHelpers; + m_needsavedsettings = rhs.m_needsavedsettings; +} + +template +CAddonDll::~CAddonDll() +{ + if (m_initialized) + Destroy(); +} + +template +AddonPtr CAddonDll::Clone() const +{ + return AddonPtr(new CAddonDll(*this)); +} + +template +bool CAddonDll::LoadDll() +{ + std::string strFileName; + if (!m_bIsChild) + { + strFileName = LibPath(); + } + else + { //FIXME hack to load same Dll twice + std::string extension = URIUtils::GetExtension(m_strLibName); + strFileName = "special://temp/" + m_strLibName; + URIUtils::RemoveExtension(strFileName); + strFileName += "-" + ID() + extension; + + if (!XFILE::CFile::Exists(strFileName)) + XFILE::CFile::Copy(LibPath(), strFileName); + + CLog::Log(LOGNOTICE, "ADDON: Loaded virtual child addon %s", strFileName.c_str()); + } + + /* Check if lib being loaded exists, else check in XBMC binary location */ +#if defined(TARGET_ANDROID) + // Android libs MUST live in this path, else multi-arch will break. + // The usual soname requirements apply. no subdirs, and filename is ^lib.*\.so$ + if (!XFILE::CFile::Exists(strFileName)) + { + std::string tempbin = getenv("XBMC_ANDROID_LIBS"); + strFileName = tempbin + "/" + m_strLibName; + } +#endif + if (!XFILE::CFile::Exists(strFileName)) + { + std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/"); + std::string tempbin = CSpecialProtocol::TranslatePath("special://xbmcbin/"); + strFileName.erase(0, temp.size()); + strFileName = tempbin + strFileName; + if (!XFILE::CFile::Exists(strFileName)) + { + CLog::Log(LOGERROR, "ADDON: Could not locate %s", m_strLibName.c_str()); + return false; + } + } + + /* Load the Dll */ + m_pDll = new TheDll; + m_pDll->SetFile(strFileName); + m_pDll->EnableDelayedUnload(false); + if (!m_pDll->Load()) + { + delete m_pDll; + m_pDll = NULL; + new CAddonStatusHandler(ID(), ADDON_STATUS_UNKNOWN, "Can't load Dll", false); + return false; + } + + m_pStruct = (TheStruct*)malloc(sizeof(TheStruct)); + if (m_pStruct) + { + ZeroMemory(m_pStruct, sizeof(TheStruct)); + m_pDll->GetAddon(m_pStruct); + return true; + } + + return false; +} + +template +ADDON_STATUS CAddonDll::Create() +{ + ADDON_STATUS status(ADDON_STATUS_UNKNOWN); + CLog::Log(LOGDEBUG, "ADDON: Dll Initializing - %s", Name().c_str()); + m_initialized = false; + + if (!LoadDll() || !CheckAPIVersion()) + return ADDON_STATUS_PERMANENT_FAILURE; + + /* Allocate the helper function class to allow crosstalk over + helper libraries */ + m_pHelpers = new CAddonCallbacks(this); + + /* Call Create to make connections, initializing data or whatever is + needed to become the AddOn running */ + try + { + status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo); + if (status == ADDON_STATUS_OK) + { + m_initialized = true; + ANNOUNCEMENT::CAnnouncementManager::Get().AddAnnouncer(this); + } + else if ((status == ADDON_STATUS_NEED_SETTINGS) || (status == ADDON_STATUS_NEED_SAVEDSETTINGS)) + { + m_needsavedsettings = (status == ADDON_STATUS_NEED_SAVEDSETTINGS); + if ((status = TransferSettings()) == ADDON_STATUS_OK) + m_initialized = true; + else + new CAddonStatusHandler(ID(), status, "", false); + } + else + { // Addon failed initialization + CLog::Log(LOGERROR, "ADDON: Dll %s - Client returned bad status (%i) from Create and is not usable", Name().c_str(), status); + new CAddonStatusHandler(ID(), status, "", false); + } + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Create"); + } + + if (!m_initialized) + SAFE_DELETE(m_pHelpers); + + return status; +} + +template +void CAddonDll::Stop() +{ + /* Inform dll to stop all activities */ + try + { + if (m_needsavedsettings) // If the addon supports it we save some settings to settings.xml before stop + { + char str_id[64] = ""; + char str_value[1024]; + CAddon::LoadUserSettings(); + for (unsigned int i=0; (strcmp(str_id,"###End") != 0); i++) + { + strcpy(str_id, "###GetSavedSettings"); + sprintf (str_value, "%i", i); + ADDON_STATUS status = m_pDll->SetSetting((const char*)&str_id, (void*)&str_value); + + if (status == ADDON_STATUS_UNKNOWN) + break; + + if (strcmp(str_id,"###End") != 0) UpdateSetting(str_id, str_value); + } + CAddon::SaveSettings(); + } + if (m_pDll) + { + m_pDll->Stop(); + CLog::Log(LOGINFO, "ADDON: Dll Stopped - %s", Name().c_str()); + } + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Stop"); + } +} + +template +void CAddonDll::Destroy() +{ + ANNOUNCEMENT::CAnnouncementManager::Get().RemoveAnnouncer(this); + + /* Unload library file */ + try + { + if (m_pDll) + { + m_pDll->Destroy(); + m_pDll->Unload(); + } + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Unload"); + } + delete m_pHelpers; + m_pHelpers = NULL; + free(m_pStruct); + m_pStruct = NULL; + if (m_pDll) + { + delete m_pDll; + m_pDll = NULL; + CLog::Log(LOGINFO, "ADDON: Dll Destroyed - %s", Name().c_str()); + } + m_initialized = false; +} + +template +bool CAddonDll::DllLoaded(void) const +{ + return m_pDll != NULL; +} + +template +ADDON_STATUS CAddonDll::GetStatus() +{ + try + { + return m_pDll->GetStatus(); + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->GetStatus()"); + } + return ADDON_STATUS_UNKNOWN; +} + +template +bool CAddonDll::LoadSettings() +{ + if (m_settingsLoaded) + return true; + + if (!LoadDll()) + return false; + + ADDON_StructSetting** sSet; + std::vector vSet; + unsigned entries = 0; + try + { + entries = m_pDll->GetSettings(&sSet); + DllUtils::StructToVec(entries, &sSet, &vSet); + m_pDll->FreeSettings(); + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->GetSettings()"); + return false; + } + + if (vSet.size()) + { + // regenerate XML doc + m_addonXmlDoc.Clear(); + TiXmlElement node("settings"); + m_addonXmlDoc.InsertEndChild(node); + + for (unsigned i=0; i < entries; i++) + { + DllSetting& setting = vSet[i]; + m_addonXmlDoc.RootElement()->InsertEndChild(MakeSetting(setting)); + } + CAddon::SettingsFromXML(m_addonXmlDoc, true); + } + else + return CAddon::LoadSettings(); + + m_settingsLoaded = true; + CAddon::LoadUserSettings(); + return true; +} + +template +TiXmlElement CAddonDll::MakeSetting(DllSetting& setting) const +{ + TiXmlElement node("setting"); + + switch (setting.type) + { + case DllSetting::CHECK: + { + node.SetAttribute("id", setting.id); + node.SetAttribute("type", "bool"); + node.SetAttribute("label", setting.label); + break; + } + case DllSetting::SPIN: + { + node.SetAttribute("id", setting.id); + node.SetAttribute("type", "enum"); + node.SetAttribute("label", setting.label); + std::string values; + for (unsigned int i = 0; i < setting.entry.size(); i++) + { + values.append(setting.entry[i]); + values.append("|"); + } + node.SetAttribute("values", values.c_str()); + break; + } + default: + break; + } + + return node; +} + +template +void CAddonDll::SaveSettings() +{ + // must save first, as TransferSettings() reloads saved settings! + CAddon::SaveSettings(); + if (m_initialized) + TransferSettings(); +} + +template +std::string CAddonDll::GetSetting(const std::string& key) +{ + return CAddon::GetSetting(key); +} + +template +ADDON_STATUS CAddonDll::TransferSettings() +{ + bool restart = false; + ADDON_STATUS reportStatus = ADDON_STATUS_OK; + + CLog::Log(LOGDEBUG, "Calling TransferSettings for: %s", Name().c_str()); + + LoadSettings(); + + const TiXmlElement *category = m_addonXmlDoc.RootElement() ? m_addonXmlDoc.RootElement()->FirstChildElement("category") : NULL; + if (!category) + category = m_addonXmlDoc.RootElement(); // no categories + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + ADDON_STATUS status = ADDON_STATUS_OK; + const char *id = setting->Attribute("id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const char *option = setting->Attribute("option"); + + if (id && !type.empty()) + { + if (type == "sep" || type == "lsep") + { + /* Don't propagate separators */ + } + else if (type == "text" || type == "ipaddress" || + type == "video" || type == "audio" || + type == "image" || type == "folder" || + type == "executable" || type == "file" || + type == "action" || type == "date" || + type == "time" || type == "select" || + type == "addon" || type == "labelenum" || + type == "fileenum" ) + { + status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str()); + } + else if (type == "enum" || type =="integer" || + type == "labelenum" || type == "rangeofnum") + { + int tmp = atoi(GetSetting(id).c_str()); + status = m_pDll->SetSetting(id, (int*) &tmp); + } + else if (type == "bool") + { + bool tmp = (GetSetting(id) == "true") ? true : false; + status = m_pDll->SetSetting(id, (bool*) &tmp); + } + else if (type == "rangeofnum" || type == "slider" || + type == "number") + { + float tmpf = (float)atof(GetSetting(id).c_str()); + int tmpi; + + if (option && strcmpi(option,"int") == 0) + { + tmpi = (int)floor(tmpf); + status = m_pDll->SetSetting(id, (int*) &tmpi); + } + else + { + status = m_pDll->SetSetting(id, (float*) &tmpf); + } + } + else + { + /* Log unknowns as an error, but go ahead and transfer the string */ + CLog::Log(LOGERROR, "Unknown setting type '%s' for %s", type.c_str(), Name().c_str()); + status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str()); + } + + if (status == ADDON_STATUS_NEED_RESTART) + restart = true; + else if (status != ADDON_STATUS_OK) + reportStatus = status; + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + + if (restart || reportStatus != ADDON_STATUS_OK) + { + new CAddonStatusHandler(ID(), restart ? ADDON_STATUS_NEED_RESTART : reportStatus, "", true); + } + + return ADDON_STATUS_OK; +} + +template +void CAddonDll::Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data) +{ + try + { + m_pDll->Announce(ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message, &data); + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Announce()"); + } +} + +template +void CAddonDll::HandleException(std::exception &e, const char* context) +{ + m_initialized = false; + m_pDll->Unload(); + CLog::Log(LOGERROR, "ADDON: Dll %s, throws an exception '%s' during %s. Contact developer '%s' with bug reports", Name().c_str(), e.what(), context, Author().c_str()); +} + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonInstaller.cpp b/xbmc/addons/AddonInstaller.cpp new file mode 100644 index 0000000..f4a241c --- /dev/null +++ b/xbmc/addons/AddonInstaller.cpp @@ -0,0 +1,974 @@ +/* + * Copyright (C) 2011-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "AddonInstaller.h" +#include "utils/log.h" +#include "utils/FileUtils.h" +#include "utils/URIUtils.h" +#include "Util.h" +#include "guilib/LocalizeStrings.h" +#include "filesystem/Directory.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "ApplicationMessenger.h" +#include "filesystem/FavouritesDirectory.h" +#include "utils/JobManager.h" +#include "dialogs/GUIDialogYesNo.h" +#include "addons/AddonManager.h" +#include "addons/Repository.h" +#include "guilib/GUIWindowManager.h" // for callback +#include "GUIUserMessages.h" // for callback +#include "utils/StringUtils.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogProgress.h" +#include "URL.h" + +#include + +using namespace std; +using namespace XFILE; +using namespace ADDON; + + +struct find_map : public binary_function +{ + bool operator() (CAddonInstaller::JobMap::value_type t, unsigned int id) const + { + return (t.second.jobID == id); + } +}; + +CAddonInstaller::CAddonInstaller() + : m_repoUpdateJob(0) +{ } + +CAddonInstaller::~CAddonInstaller() +{ } + +CAddonInstaller &CAddonInstaller::Get() +{ + static CAddonInstaller addonInstaller; + return addonInstaller; +} + +void CAddonInstaller::OnJobComplete(unsigned int jobID, bool success, CJob* job) +{ + if (success) + CAddonMgr::Get().FindAddons(); + + CSingleLock lock(m_critSection); + if (strncmp(job->GetType(), "repoupdate", 10) == 0) + { + // repo job finished + m_repoUpdateDone.Set(); + m_repoUpdateJob = 0; + lock.Leave(); + } + else + { + // download job + JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID)); + if (i != m_downloadJobs.end()) + m_downloadJobs.erase(i); + lock.Leave(); + PrunePackageCache(); + } + + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); + g_windowManager.SendThreadMessage(msg); +} + +void CAddonInstaller::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job) +{ + CSingleLock lock(m_critSection); + JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID)); + if (i != m_downloadJobs.end()) + { + // update job progress + i->second.progress = progress; + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM); + msg.SetStringParam(i->first); + lock.Leave(); + g_windowManager.SendThreadMessage(msg); + } +} + +bool CAddonInstaller::IsDownloading() const +{ + CSingleLock lock(m_critSection); + return !m_downloadJobs.empty(); +} + +void CAddonInstaller::GetInstallList(VECADDONS &addons) const +{ + CSingleLock lock(m_critSection); + vector addonIDs; + for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i) + { + if (i->second.jobID) + addonIDs.push_back(i->first); + } + lock.Leave(); + + CAddonDatabase database; + database.Open(); + for (vector::iterator it = addonIDs.begin(); it != addonIDs.end(); ++it) + { + AddonPtr addon; + if (database.GetAddon(*it, addon)) + addons.push_back(addon); + } +} + +bool CAddonInstaller::GetProgress(const std::string &addonID, unsigned int &percent) const +{ + CSingleLock lock(m_critSection); + JobMap::const_iterator i = m_downloadJobs.find(addonID); + if (i != m_downloadJobs.end()) + { + percent = i->second.progress; + return true; + } + return false; +} + +bool CAddonInstaller::Cancel(const std::string &addonID) +{ + CSingleLock lock(m_critSection); + JobMap::iterator i = m_downloadJobs.find(addonID); + if (i != m_downloadJobs.end()) + { + CJobManager::GetInstance().CancelJob(i->second.jobID); + m_downloadJobs.erase(i); + return true; + } + + return false; +} + +bool CAddonInstaller::InstallModal(const std::string &addonID, ADDON::AddonPtr &addon, bool promptForInstall /* = true */) +{ + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + + // we assume that addons that are enabled don't get to this routine (i.e. that GetAddon() has been called) + if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false)) + return false; // addon is installed but disabled, and the user has specifically activated something that needs + // the addon - should we enable it? + + // check we have it available + CAddonDatabase database; + database.Open(); + if (!database.GetAddon(addonID, addon)) + return false; + + // if specified ask the user if he wants it installed + if (promptForInstall && + !CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100), + addon->Name().c_str(), g_localizeStrings.Get(24101))) + return false; + + if (!Install(addonID, true, "", false, true)) + return false; + + return CAddonMgr::Get().GetAddon(addonID, addon); +} + +bool CAddonInstaller::Install(const std::string &addonID, bool force /* = false */, const std::string &referer /* = "" */, bool background /* = true */, bool modal /* = false */) +{ + AddonPtr addon; + bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false); + if (addonInstalled && !force) + return true; + + if (referer.empty()) + { + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + } + + // check whether we have it available in a repository + std::string hash; + if (!CAddonInstallJob::GetAddonWithHash(addonID, addon, hash)) + return false; + + return DoInstall(addon, hash, addonInstalled, referer, background, modal); +} + +bool CAddonInstaller::DoInstall(const AddonPtr &addon, const std::string &hash /* = "" */, bool update /* = false */, const std::string &referer /* = "" */, bool background /* = true */, bool modal /* = false */) +{ + // check whether we already have the addon installing + CSingleLock lock(m_critSection); + if (m_downloadJobs.find(addon->ID()) != m_downloadJobs.end()) + return false; + + CAddonInstallJob* installJob = new CAddonInstallJob(addon, hash, update, referer); + if (background) + { + unsigned int jobID = CJobManager::GetInstance().AddJob(installJob, this); + m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID))); + return true; + } + + m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0))); + lock.Leave(); + + bool result = false; + if (modal) + result = installJob->DoModal(); + else + result = installJob->DoWork(); + delete installJob; + + lock.Enter(); + JobMap::iterator i = m_downloadJobs.find(addon->ID()); + m_downloadJobs.erase(i); + + return result; +} + +bool CAddonInstaller::InstallFromZip(const std::string &path) +{ + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + + // grab the descriptive XML document from the zip, and read it in + CFileItemList items; + // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol + CURL pathToUrl(path); + CURL zipDir = URIUtils::CreateArchivePath("zip", pathToUrl, ""); + if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder) + { + CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false); + return false; + } + + // TODO: possibly add support for github generated zips here? + std::string archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml"); + + CXBMCTinyXML xml; + AddonPtr addon; + if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon)) + { + // set the correct path + addon->Props().path = items[0]->GetPath(); + addon->Props().icon = URIUtils::AddFileToFolder(items[0]->GetPath(), "icon.png"); + + // install the addon + return DoInstall(addon); + } + + CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false); + return false; +} + +void CAddonInstaller::InstallFromXBMCRepo(const set &addonIDs) +{ + // first check we have the our repositories up to date (and wait until we do) + UpdateRepos(false, true); + + // now install the addons + for (set::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i) + Install(*i); +} + +bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, CAddonDatabase *database /* = NULL */) +{ + std::vector preDeps; + preDeps.push_back(addon->ID()); + CAddonDatabase localDB; + if (!database) + database = &localDB; + + return CheckDependencies(addon, preDeps, *database); +} + +bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, + std::vector& preDeps, CAddonDatabase &database) +{ + if (addon == NULL) + return true; // a NULL addon has no dependencies + + if (!database.Open()) + return false; + + ADDONDEPS deps = addon->GetDeps(); + for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) + { + const std::string &addonID = i->first; + const AddonVersion &version = i->second.first; + bool optional = i->second.second; + AddonPtr dep; + bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep); + if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional)) + { + // we have it but our version isn't good enough, or we don't have it and we need it + if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version)) + { + // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied. + CLog::Log(LOGDEBUG, "CAddonInstallJob[%s]: requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.asString().c_str()); + database.Close(); + return false; + } + } + + // at this point we have our dep, or the dep is optional (and we don't have it) so check that it's OK as well + // TODO: should we assume that installed deps are OK? + if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end()) + { + if (!CheckDependencies(dep, preDeps, database)) + { + database.Close(); + preDeps.push_back(dep->ID()); + return false; + } + } + } + database.Close(); + + return true; +} + +CDateTime CAddonInstaller::LastRepoUpdate() const +{ + CDateTime update; + CAddonDatabase database; + if (!database.Open()) + return update; + + VECADDONS addons; + CAddonMgr::Get().GetAddons(ADDON_REPOSITORY, addons); + for (unsigned int i = 0; i < addons.size(); i++) + { + CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID()); + if (lastUpdate.IsValid() && lastUpdate > update) + update = lastUpdate; + } + + return update; +} + +void CAddonInstaller::UpdateRepos(bool force, bool wait) +{ + CSingleLock lock(m_critSection); + if (m_repoUpdateJob) + { + if (wait) + { + // wait for our job to complete + lock.Leave(); + CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__); + m_repoUpdateDone.Wait(); + } + return; + } + + // don't run repo update jobs while on the login screen which runs under the master profile + if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN) + return; + + if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600) + return; + + CAddonDatabase database; + if (!database.Open()) + return; + + m_repoUpdateWatch.StartZero(); + + VECADDONS addons; + if (CAddonMgr::Get().GetAddons(ADDON_REPOSITORY, addons)) + { + for (const auto& repo : addons) + { + CDateTime lastUpdate = database.GetRepoTimestamp(repo->ID()); + if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0, 24, 0, 0) < CDateTime::GetCurrentDateTime() + || repo->Version() != database.GetRepoVersion(repo->ID())) + { + CLog::Log(LOGDEBUG, "Checking repositories for updates (triggered by %s)", repo->Name().c_str()); + m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this); + if (wait) + { + // wait for our job to complete + lock.Leave(); + CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__); + m_repoUpdateDone.Wait(); + } + + return; + } + } + } +} + +bool CAddonInstaller::HasJob(const std::string& ID) const +{ + CSingleLock lock(m_critSection); + return m_downloadJobs.find(ID) != m_downloadJobs.end(); +} + +void CAddonInstaller::PrunePackageCache() +{ + std::map packs; + int64_t size = EnumeratePackageFolder(packs); + int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize * 1024 * 1024; + if (size < limit) + return; + + // Prune packages + // 1. Remove the largest packages, leaving at least 2 for each add-on + CFileItemList items; + CAddonDatabase db; + db.Open(); + for (std::map::const_iterator it = packs.begin(); it != packs.end(); ++it) + { + it->second->Sort(SortByLabel, SortOrderDescending); + for (int j = 2; j < it->second->Size(); j++) + items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j)))); + } + + items.Sort(SortBySize, SortOrderDescending); + int i = 0; + while (size > limit && i < items.Size()) + { + size -= items[i]->m_dwSize; + db.RemovePackage(items[i]->GetPath()); + CFileUtils::DeleteItem(items[i++], true); + } + + if (size > limit) + { + // 2. Remove the oldest packages (leaving least 1 for each add-on) + items.Clear(); + for (std::map::iterator it = packs.begin(); it != packs.end(); ++it) + { + if (it->second->Size() > 1) + items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1)))); + } + + items.Sort(SortByDate, SortOrderAscending); + i = 0; + while (size > limit && i < items.Size()) + { + size -= items[i]->m_dwSize; + db.RemovePackage(items[i]->GetPath()); + CFileUtils::DeleteItem(items[i++],true); + } + } + + // clean up our mess + for (std::map::iterator it = packs.begin(); it != packs.end(); ++it) + delete it->second; +} + +int64_t CAddonInstaller::EnumeratePackageFolder(std::map& result) +{ + CFileItemList items; + CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS); + int64_t size = 0; + for (int i = 0; i < items.Size(); i++) + { + if (items[i]->m_bIsFolder) + continue; + + size += items[i]->m_dwSize; + std::string pack,dummy; + AddonVersion::SplitFileName(pack, dummy, items[i]->GetLabel()); + if (result.find(pack) == result.end()) + result[pack] = new CFileItemList; + result[pack]->Add(CFileItemPtr(new CFileItem(*items[i]))); + } + + return size; +} + +CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const std::string &hash /* = "" */, bool update /* = false */, const std::string &referer /* = "" */) + : m_addon(addon), + m_hash(hash), + m_update(update), + m_referer(referer) +{ } + +AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon) +{ + AddonPtr repoPtr; + + CAddonDatabase database; + if (!database.Open()) + return repoPtr; + + std::string repo; + if (!database.GetRepoForAddon(addon->ID(), repo)) + return repoPtr; + + if (!CAddonMgr::Get().GetAddon(repo, repoPtr)) + return repoPtr; + + if (std::dynamic_pointer_cast(repoPtr) == NULL) + { + repoPtr.reset(); + return repoPtr; + } + + return repoPtr; +} + +bool CAddonInstallJob::GetAddonWithHash(const std::string& addonID, ADDON::AddonPtr& addon, std::string& hash) +{ + CAddonDatabase database; + if (!database.Open()) + return false; + + if (!database.GetAddon(addonID, addon)) + return false; + + AddonPtr ptr = GetRepoForAddon(addon); + if (ptr == NULL) + return false; + + RepositoryPtr repo = std::dynamic_pointer_cast(ptr); + if (repo == NULL) + return false; + + hash = repo->GetAddonHash(addon); + return true; +} + +bool CAddonInstallJob::DoWork() +{ + SetTitle(StringUtils::Format(g_localizeStrings.Get(24057).c_str(), m_addon->Name().c_str())); + SetProgress(0); + + // check whether all the dependencies are available or not + SetText(g_localizeStrings.Get(24058)); + if (!CAddonInstaller::Get().CheckDependencies(m_addon)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: dependency check failed", m_addon->ID().c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24044)); + return false; + } + + AddonPtr repoPtr = GetRepoForAddon(m_addon); + std::string installFrom; + if (!repoPtr || repoPtr->Props().libname.empty()) + { + // Addons are installed by downloading the .zip package on the server to the local + // packages folder, then extracting from the local .zip package into the addons folder + // Both these functions are achieved by "copying" using the vfs. + + std::string dest = "special://home/addons/packages/"; + std::string package = URIUtils::AddFileToFolder("special://home/addons/packages/", + URIUtils::GetFileName(m_addon->Path())); + if (URIUtils::HasSlashAtEnd(m_addon->Path())) + { // passed in a folder - all we need do is copy it across + installFrom = m_addon->Path(); + } + else + { + CAddonDatabase db; + if (!db.Open()) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to open database", m_addon->ID().c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID()); + return false; + } + + std::string md5; + // check that we don't already have a valid copy + if (!m_hash.empty() && CFile::Exists(package)) + { + if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5) + { + db.RemovePackage(package); + CFile::Delete(package); + } + } + + // zip passed in - download + extract + if (!CFile::Exists(package)) + { + std::string path(m_addon->Path()); + if (!m_referer.empty() && URIUtils::IsInternetStream(path)) + { + CURL url(path); + url.SetProtocolOptions(m_referer); + path = url.Get(); + } + + if (!DownloadPackage(path, dest)) + { + CFile::Delete(package); + + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to download %s", m_addon->ID().c_str(), package.c_str()); + ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); + return false; + } + } + + // at this point we have the package - check that it is valid + SetText(g_localizeStrings.Get(24077)); + if (!m_hash.empty()) + { + md5 = CUtil::GetFileMD5(package); + if (!StringUtils::EqualsNoCase(md5, m_hash)) + { + CFile::Delete(package); + + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: MD5 mismatch after download %s", m_addon->ID().c_str(), package.c_str()); + ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); + return false; + } + + db.AddPackage(m_addon->ID(), package, md5); + } + + // check the archive as well - should have just a single folder in the root + CURL archive = URIUtils::CreateArchivePath("zip", CURL(package), ""); + + CFileItemList archivedFiles; + CDirectory::GetDirectory(archive, archivedFiles); + + if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder) + { + // invalid package + db.RemovePackage(package); + CFile::Delete(package); + + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: invalid package %s", m_addon->ID().c_str(), package.c_str()); + ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); + return false; + } + + installFrom = archivedFiles[0]->GetPath(); + } + repoPtr.reset(); + } + + // run any pre-install functions + bool reloadAddon = OnPreInstall(); + + // perform install + if (!Install(installFrom, repoPtr)) + return false; + + // run any post-install guff + OnPostInstall(reloadAddon); + + // and we're done! + MarkFinished(); + return true; +} + +bool CAddonInstallJob::DownloadPackage(const std::string &path, const std::string &dest) +{ + if (ShouldCancel(0, 1)) + return false; + + SetText(g_localizeStrings.Get(24078)); + + // need to download/copy the package first + CFileItemList list; + list.Add(CFileItemPtr(new CFileItem(path, false))); + list[0]->Select(true); + + return DoFileOperation(CFileOperationJob::ActionReplace, list, dest, true); +} + +bool CAddonInstallJob::DoFileOperation(FileAction action, CFileItemList &items, const std::string &file, bool useSameJob /* = true */) +{ + bool result = false; + if (useSameJob) + { + SetFileOperation(action, items, file); + + // temporarily disable auto-closing so not to close the current progress indicator + bool autoClose = GetAutoClose(); + if (autoClose) + SetAutoClose(false); + // temporarily disable updating title or text + bool updateInformation = GetUpdateInformation(); + if (updateInformation) + SetUpdateInformation(false); + + result = CFileOperationJob::DoWork(); + + SetUpdateInformation(updateInformation); + SetAutoClose(autoClose); + } + else + { + CFileOperationJob job(action, items, file); + + // pass our progress indicators to the temporary job and only allow it to + // show progress updates (no title or text changes) + job.SetProgressIndicators(GetProgressBar(), GetProgressDialog(), GetUpdateProgress(), false); + + result = job.DoWork(); + } + + return result; +} + +bool CAddonInstallJob::OnPreInstall() +{ + return m_addon->OnPreInstall(); +} + +bool CAddonInstallJob::DeleteAddon(const std::string &addonFolder) +{ + CFileItemList list; + list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); + list[0]->Select(true); + + return DoFileOperation(CFileOperationJob::ActionDelete, list, "", false); +} + +bool CAddonInstallJob::Install(const std::string &installFrom, const AddonPtr& repo) +{ + SetText(g_localizeStrings.Get(24079)); + ADDONDEPS deps = m_addon->GetDeps(); + + unsigned int totalSteps = static_cast(deps.size()); + if (ShouldCancel(0, totalSteps)) + return false; + + // The first thing we do is install dependencies + std::string referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().asString().c_str()); + for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it) + { + if (it->first != "xbmc.metadata") + { + const std::string &addonID = it->first; + const AddonVersion &version = it->second.first; + bool optional = it->second.second; + AddonPtr dependency; + bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency); + if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional)) + { + // we have it but our version isn't good enough, or we don't have it and we need it + bool force = dependency != NULL; + + // dependency is already queued up for install - ::Install will fail + // instead we wait until the Job has finished. note that we + // recall install on purpose in case prior installation failed + if (CAddonInstaller::Get().HasJob(addonID)) + { + while (CAddonInstaller::Get().HasJob(addonID)) + Sleep(50); + force = false; + + if (!CAddonMgr::Get().IsAddonInstalled(addonID)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + } + // don't have the addon or the addon isn't new enough - grab it (no new job for these) + else if (IsModal()) + { + AddonPtr addon; + std::string hash; + if (!CAddonInstallJob::GetAddonWithHash(addonID, addon, hash)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to find dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + + CAddonInstallJob dependencyJob(addon, hash, force, referer); + + // pass our progress indicators to the temporary job and don't allow it to + // show progress or information updates (no progress, title or text changes) + dependencyJob.SetProgressIndicators(GetProgressBar(), GetProgressDialog(), false, false); + + if (!dependencyJob.DoModal()) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + } + else if (!CAddonInstaller::Get().Install(addonID, force, referer, false)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + } + } + + if (ShouldCancel(std::distance(deps.begin(), it), totalSteps)) + return false; + } + + SetText(g_localizeStrings.Get(24086)); + SetProgress(0); + + // now that we have all our dependencies, we can install our add-on + if (repo != NULL) + { + CFileItemList dummy; + std::string s = StringUtils::Format("plugin://%s/?action=install&package=%s&version=%s", repo->ID().c_str(), + m_addon->ID().c_str(), m_addon->Version().asString().c_str()); + if (!CDirectory::GetDirectory(s, dummy)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: installation of repository failed", m_addon->ID().c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID()); + return false; + } + } + else + { + std::string addonFolder = installFrom; + URIUtils::RemoveSlashAtEnd(addonFolder); + addonFolder = URIUtils::AddFileToFolder("special://home/addons/", URIUtils::GetFileName(addonFolder)); + + CFileItemList install; + install.Add(CFileItemPtr(new CFileItem(installFrom, true))); + install[0]->Select(true); + + AddonPtr addon; + if (!DoFileOperation(CFileOperationJob::ActionReplace, install, "special://home/addons/", false) || + !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) + { + // failed extraction or failed to load addon description + DeleteAddon(addonFolder); + + std::string addonID = URIUtils::GetFileName(addonFolder); + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: could not read addon description of %s", addonID.c_str(), addonFolder.c_str()); + ReportInstallError(addonID, addonID); + return false; + } + + // Update the addon manager so that it has the newly installed add-on. + CAddonMgr::Get().FindAddons(); + } + SetProgress(100); + + return true; +} + +void CAddonInstallJob::OnPostInstall(bool reloadAddon) +{ + if (!IsModal() && CSettings::Get().GetBool("general.addonnotifications")) + CGUIDialogKaiToast::QueueNotification(m_addon->Icon(), m_addon->Name(), + g_localizeStrings.Get(m_update ? 24065 : 24064), + TOAST_DISPLAY_TIME, false, TOAST_DISPLAY_TIME); + + m_addon->OnPostInstall(reloadAddon, m_update, IsModal()); +} + +void CAddonInstallJob::ReportInstallError(const std::string& addonID, const std::string& fileName, const std::string& message /* = "" */) +{ + AddonPtr addon; + CAddonDatabase database; + if (database.Open()) + { + database.GetAddon(addonID, addon); + database.Close(); + } + + MarkFinished(); + + std::string msg = message; + if (addon != NULL) + { + AddonPtr addon2; + CAddonMgr::Get().GetAddon(addonID, addon2); + if (msg.empty()) + msg = g_localizeStrings.Get(addon2 != NULL ? 113 : 114); + + if (IsModal()) + CGUIDialogOK::ShowAndGetInput(m_addon->Name(), msg); + else + CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), msg, TOAST_DISPLAY_TIME, false); + } + else + { + if (msg.empty()) + msg = g_localizeStrings.Get(114); + if (IsModal()) + CGUIDialogOK::ShowAndGetInput(fileName, msg); + else + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, fileName, msg, TOAST_DISPLAY_TIME, false); + } +} + +std::string CAddonInstallJob::AddonID() const +{ + return m_addon ? m_addon->ID() : ""; +} + +CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon) + : m_addon(addon) +{ } + +bool CAddonUnInstallJob::DoWork() +{ + m_addon->OnPreUnInstall(); + + AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon); + RepositoryPtr therepo = std::dynamic_pointer_cast(repoPtr); + if (therepo != NULL && !therepo->Props().libname.empty()) + { + CFileItemList dummy; + std::string s = StringUtils::Format("plugin://%s/?action=uninstall&package=%s", therepo->ID().c_str(), m_addon->ID().c_str()); + if (!CDirectory::GetDirectory(s, dummy)) + return false; + } + else if (!DeleteAddon(m_addon->Path())) + return false; + + OnPostUnInstall(); + + return true; +} + +bool CAddonUnInstallJob::DeleteAddon(const std::string &addonFolder) +{ + CFileItemList list; + list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); + list[0]->Select(true); + + SetFileOperation(CFileOperationJob::ActionDelete, list, ""); + return CFileOperationJob::DoWork(); +} + +void CAddonUnInstallJob::OnPostUnInstall() +{ + bool bSave = false; + CFileItemList items; + XFILE::CFavouritesDirectory::Load(items); + for (int i = 0; i < items.Size(); i++) + { + if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos) + { + items.Remove(items[i].get()); + bSave = true; + } + } + + if (bSave) + CFavouritesDirectory::Save(items); + + m_addon->OnPostUnInstall(); +} diff --git a/xbmc/addons/AddonInstaller.h b/xbmc/addons/AddonInstaller.h new file mode 100644 index 0000000..aca93d8 --- /dev/null +++ b/xbmc/addons/AddonInstaller.h @@ -0,0 +1,229 @@ +#pragma once +/* + * Copyright (C) 2011-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "utils/FileOperationJob.h" +#include "addons/Addon.h" +#include "utils/Stopwatch.h" +#include "threads/Event.h" + +class CAddonDatabase; + +enum { + AUTO_UPDATES_ON = 0, + AUTO_UPDATES_NOTIFY, + AUTO_UPDATES_NEVER, + AUTO_UPDATES_MAX +}; + +class CAddonInstaller : public IJobCallback +{ +public: + static CAddonInstaller &Get(); + + bool IsDownloading() const; + void GetInstallList(ADDON::VECADDONS &addons) const; + bool GetProgress(const std::string &addonID, unsigned int &percent) const; + bool Cancel(const std::string &addonID); + + /*! \brief Installs the addon while showing a modal progress dialog + \param addonID the addon ID of the item to install. + \param addon [out] the installed addon for later use. + \param promptForInstall Whether or not to prompt the user before installing the addon. + \return true on successful install, false otherwise. + \sa Install + */ + bool InstallModal(const std::string &addonID, ADDON::AddonPtr &addon, bool promptForInstall = true); + + /*! \brief Install an addon if it is available in a repository + \param addonID the addon ID of the item to install + \param force whether to force the install even if the addon is already installed (eg for updating). Defaults to false. + \param referer string to use for referer for http fetch. Set to previous version when updating, parent when fetching a dependency + \param background whether to install in the background or not. Defaults to true. + \param modal whether to show a modal dialog when not installing in background + \return true on successful install, false on failure. + \sa DoInstall + */ + bool Install(const std::string &addonID, bool force = false, const std::string &referer="", bool background = true, bool modal = false); + + /*! \brief Install an addon from the given zip path + \param path the zip file to install from + \return true if successful, false otherwise + \sa DoInstall + */ + bool InstallFromZip(const std::string &path); + + /*! \brief Install a set of addons from the official repository (if needed) + \param addonIDs a set of addon IDs to install + */ + void InstallFromXBMCRepo(const std::set &addonIDs); + + /*! \brief Check whether dependencies of an addon exist or are installable. + Iterates through the addon's dependencies, checking they're installed or installable. + Each dependency must also satisfies CheckDependencies in turn. + \param addon the addon to check + \param database the database instance to update. Defaults to NULL. + \return true if dependencies are available, false otherwise. + */ + bool CheckDependencies(const ADDON::AddonPtr &addon, CAddonDatabase *database = NULL); + + /*! \brief Update all repositories (if needed) + Runs through all available repositories and queues an update of them if they + need it (according to the set timeouts) or if forced. Optionally busy wait + until the repository updates are complete. + \param force whether we should run an update regardless of the normal update cycle. Defaults to false. + \param wait whether we should busy wait for the updates to be performed. Defaults to false. + */ + + /*! \brief Check if an installation job for a given add-on is already queued up + * \param ID The ID of the add-on + * \return true if a job exists, false otherwise + */ + bool HasJob(const std::string& ID) const; + + /*! \brief Fetch the last repository update time. + \return the last time a repository was updated. + */ + CDateTime LastRepoUpdate() const; + void UpdateRepos(bool force = false, bool wait = false); + + void OnJobComplete(unsigned int jobID, bool success, CJob* job); + void OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job); + + class CDownloadJob + { + public: + CDownloadJob(unsigned int id) + { + jobID = id; + progress = 0; + } + unsigned int jobID; + unsigned int progress; + }; + + typedef std::map JobMap; + +private: + // private construction, and no assignements; use the provided singleton methods + CAddonInstaller(); + CAddonInstaller(const CAddonInstaller&); + CAddonInstaller const& operator=(CAddonInstaller const&); + virtual ~CAddonInstaller(); + + /*! \brief Install an addon from a repository or zip + \param addon the AddonPtr describing the addon + \param hash the hash to verify the install. Defaults to "". + \param update whether this is an update of an existing addon, or a new install. Defaults to false. + \param referer string to use for referer for http fetch. Defaults to "". + \param background whether to install in the background or not. Defaults to true. + \return true on successful install, false on failure. + */ + bool DoInstall(const ADDON::AddonPtr &addon, const std::string &hash = "", bool update = false, const std::string &referer = "", bool background = true, bool modal = false); + + /*! \brief Check whether dependencies of an addon exist or are installable. + Iterates through the addon's dependencies, checking they're installed or installable. + Each dependency must also satisfies CheckDependencies in turn. + \param addon the addon to check + \param preDeps previous dependencies encountered during recursion. aids in avoiding infinite recursion + \param database database instance to update + \return true if dependencies are available, false otherwise. + */ + bool CheckDependencies(const ADDON::AddonPtr &addon, std::vector& preDeps, CAddonDatabase &database); + + void PrunePackageCache(); + int64_t EnumeratePackageFolder(std::map& result); + + CCriticalSection m_critSection; + JobMap m_downloadJobs; + CStopWatch m_repoUpdateWatch; ///< repository updates are done based on this counter + unsigned int m_repoUpdateJob; ///< the job ID of the repository updates + CEvent m_repoUpdateDone; ///< event set when the repository updates are complete +}; + +class CAddonInstallJob : public CFileOperationJob +{ +public: + CAddonInstallJob(const ADDON::AddonPtr &addon, const std::string &hash = "", bool update = false, const std::string &referer = ""); + + virtual bool DoWork(); + + /*! \brief return the id of the addon being installed + \return id of the installing addon + */ + std::string AddonID() const; + + /*! \brief Find which repository hosts an add-on + * \param addon The add-on to find the repository for + * \return The hosting repository + */ + static ADDON::AddonPtr GetRepoForAddon(const ADDON::AddonPtr& addon); + + /*! \brief Find the add-on and itshash for the given add-on ID + * \param addonID ID of the add-on to find + * \param addon Add-on with the given add-on ID + * \param hash Hash of the add-on + * \return True if the add-on and its hash were found, false otherwise. + */ + static bool GetAddonWithHash(const std::string& addonID, ADDON::AddonPtr& addon, std::string& hash); + +private: + bool OnPreInstall(); + void OnPostInstall(bool reloadAddon); + bool Install(const std::string &installFrom, const ADDON::AddonPtr& repo = ADDON::AddonPtr()); + bool DownloadPackage(const std::string &path, const std::string &dest); + + /*! \brief Delete an addon following install failure + \param addonFolder - the folder to delete + */ + bool DeleteAddon(const std::string &addonFolder); + + bool DoFileOperation(FileAction action, CFileItemList &items, const std::string &file, bool useSameJob = true); + + /*! \brief Queue a notification for addon installation/update failure + \param addonID - addon id + \param fileName - filename which is shown in case the addon id is unknown + \param message - error message to be displayed + */ + void ReportInstallError(const std::string& addonID, const std::string& fileName, const std::string& message = ""); + + ADDON::AddonPtr m_addon; + std::string m_hash; + bool m_update; + std::string m_referer; +}; + +class CAddonUnInstallJob : public CFileOperationJob +{ +public: + CAddonUnInstallJob(const ADDON::AddonPtr &addon); + + virtual bool DoWork(); + +private: + /*! \brief Delete an addon following install failure + \param addonFolder - the folder to delete + */ + bool DeleteAddon(const std::string &addonFolder); + + void OnPostUnInstall(); + + ADDON::AddonPtr m_addon; +}; diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp new file mode 100644 index 0000000..9ab5c31 --- /dev/null +++ b/xbmc/addons/AddonManager.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "AddonManager.h" +#include "Addon.h" +#include "AudioEncoder.h" +#include "DllLibCPluff.h" +#include "utils/StringUtils.h" +#include "utils/JobManager.h" +#include "threads/SingleLock.h" +#include "FileItem.h" +#include "LangInfo.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/XBMCTinyXML.h" +#ifdef HAS_VISUALISATION +#include "Visualisation.h" +#endif +#ifdef HAS_SCREENSAVER +#include "ScreenSaver.h" +#endif +#ifdef HAS_PVRCLIENTS +#include "DllPVRClient.h" +#include "pvr/addons/PVRClient.h" +#endif +//#ifdef HAS_SCRAPERS +#include "Scraper.h" +//#endif +#include "PluginSource.h" +#include "Repository.h" +#include "Skin.h" +#include "Service.h" +#include "ContextItemAddon.h" +#include "Util.h" +#include "addons/Webinterface.h" + +using namespace std; +using namespace XFILE; + +namespace ADDON +{ + +cp_log_severity_t clog_to_cp(int lvl); +void cp_fatalErrorHandler(const char *msg); +void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data); + +/********************************************************** + * CAddonMgr + * + */ + +map CAddonMgr::m_managers; + +AddonPtr CAddonMgr::Factory(const cp_extension_t *props) +{ + if (!PlatformSupportsAddon(props->plugin)) + return AddonPtr(); + + /* Check if user directories need to be created */ + const cp_cfg_element_t *settings = GetExtElement(props->configuration, "settings"); + if (settings) + CheckUserDirs(settings); + + const TYPE type = TranslateType(props->ext_point_id); + switch (type) + { + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return AddonPtr(new CPluginSource(props)); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + return AddonPtr(new CAddon(props)); + case ADDON_WEB_INTERFACE: + return AddonPtr(new CWebinterface(props)); + case ADDON_SCRIPT_WEATHER: + { + // Eden (API v2.0) broke old weather add-ons + AddonPtr result(new CAddon(props)); + AddonVersion ver1 = result->GetDependencyVersion("xbmc.python"); + AddonVersion ver2 = AddonVersion("2.0"); + if (ver1 < ver2) + { + CLog::Log(LOGINFO,"%s: Weather add-ons for api < 2.0 unsupported (%s)",__FUNCTION__,result->ID().c_str()); + return AddonPtr(); + } + return result; + } + case ADDON_SERVICE: + return AddonPtr(new CService(props)); + 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 AddonPtr(new CScraper(props)); + case ADDON_VIZ: + case ADDON_SCREENSAVER: + case ADDON_PVRDLL: + case ADDON_AUDIOENCODER: + { // begin temporary platform handling for Dlls + // ideally platforms issues will be handled by C-Pluff + // this is not an attempt at a solution + std::string value; + if (type == ADDON_SCREENSAVER && 0 == strnicmp(props->plugin->identifier, "screensaver.xbmc.builtin.", 25)) + { // built in screensaver + return AddonPtr(new CAddon(props)); + } + if (type == ADDON_SCREENSAVER) + { // Python screensaver + std::string library = CAddonMgr::Get().GetExtValue(props->configuration, "@library"); + if (URIUtils::HasExtension(library, ".py")) + return AddonPtr(new CScreenSaver(props)); + } + if (type == ADDON_AUDIOENCODER && 0 == strncmp(props->plugin->identifier, + "audioencoder.xbmc.builtin.", 26)) + { // built in audio encoder + return AddonPtr(new CAudioEncoder(props)); + } + std::string tograb; +#if defined(TARGET_ANDROID) + tograb = "@library_android"; +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + tograb = "@library_linux"; +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + tograb = "@library_windx"; +#elif defined(TARGET_DARWIN) + tograb = "@library_osx"; +#endif + value = GetExtValue(props->plugin->extensions->configuration, tograb.c_str()); + if (value.empty()) + break; + if (type == ADDON_VIZ) + { +#if defined(HAS_VISUALISATION) + return AddonPtr(new CVisualisation(props)); +#endif + } + else if (type == ADDON_PVRDLL) + { +#ifdef HAS_PVRCLIENTS + return AddonPtr(new PVR::CPVRClient(props)); +#endif + } + else if (type == ADDON_AUDIOENCODER) + return AddonPtr(new CAudioEncoder(props)); + else + return AddonPtr(new CScreenSaver(props)); + } + case ADDON_SKIN: + return AddonPtr(new CSkinInfo(props)); + case ADDON_VIZ_LIBRARY: + return AddonPtr(new CAddonLibrary(props)); + case ADDON_REPOSITORY: + return AddonPtr(new CRepository(props)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(props)); + default: + break; + } + return AddonPtr(); +} + +bool CAddonMgr::CheckUserDirs(const cp_cfg_element_t *settings) +{ + if (!settings) + return false; + + const cp_cfg_element_t *userdirs = GetExtElement((cp_cfg_element_t *)settings, "userdirs"); + if (!userdirs) + return false; + + ELEMENTS elements; + if (!GetExtElements((cp_cfg_element_t *)userdirs, "userdir", elements)) + return false; + + ELEMENTS::iterator itr = elements.begin(); + while (itr != elements.end()) + { + std::string path = GetExtValue(*itr++, "@path"); + if (!CDirectory::Exists(path)) + { + if (!CUtil::CreateDirectoryEx(path)) + { + CLog::Log(LOGERROR, "CAddonMgr::CheckUserDirs: Unable to create directory %s.", path.c_str()); + return false; + } + } + } + + return true; +} + +CAddonMgr::CAddonMgr() +{ + m_cpluff = NULL; +} + +CAddonMgr::~CAddonMgr() +{ + DeInit(); +} + +CAddonMgr &CAddonMgr::Get() +{ + static CAddonMgr sAddonMgr; + return sAddonMgr; +} + +IAddonMgrCallback* CAddonMgr::GetCallbackForType(TYPE type) +{ + if (m_managers.find(type) == m_managers.end()) + return NULL; + else + return m_managers[type]; +} + +bool CAddonMgr::RegisterAddonMgrCallback(const TYPE type, IAddonMgrCallback* cb) +{ + if (cb == NULL) + return false; + + m_managers.erase(type); + m_managers[type] = cb; + + return true; +} + +void CAddonMgr::UnregisterAddonMgrCallback(TYPE type) +{ + m_managers.erase(type); +} + +bool CAddonMgr::Init() +{ + m_cpluff = new DllLibCPluff; + m_cpluff->Load(); + + m_database.Open(); + + if (!m_cpluff->IsLoaded()) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, could not load libcpluff"); + return false; + } + + m_cpluff->set_fatal_error_handler(cp_fatalErrorHandler); + + cp_status_t status; + status = m_cpluff->init(); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_init() returned status: %i", status); + return false; + } + + //TODO could separate addons into different contexts + // would allow partial unloading of addon framework + m_cp_context = m_cpluff->create_context(&status); + assert(m_cp_context); + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://home/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmc/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmcbin/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_logger(m_cp_context, cp_logger, + &CAddonMgr::Get(), clog_to_cp(g_advancedSettings.m_logLevel)); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_logger() returned status: %i", status); + return false; + } + + FindAddons(); + + VECADDONS repos; + if (GetAddons(ADDON_REPOSITORY, repos)) + { + VECADDONS::iterator it = repos.begin(); + for (;it != repos.end(); ++it) + CLog::Log(LOGNOTICE, "ADDONS: Using repository %s", (*it)->ID().c_str()); + } + + return true; +} + +void CAddonMgr::DeInit() +{ + if (m_cpluff) + m_cpluff->destroy(); + delete m_cpluff; + m_cpluff = NULL; + m_database.Close(); + m_disabled.clear(); +} + +bool CAddonMgr::HasAddons(const TYPE &type, bool enabled /*= true*/) +{ + // TODO: This isn't particularly efficient as we create an addon type for each addon using the Factory, just so + // we can check addon dependencies in the addon constructor. + VECADDONS addons; + return GetAddons(type, addons, enabled); +} + +bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */) +{ + for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i) + { + if (!allowRepos && ADDON_REPOSITORY == (TYPE)i) + continue; + VECADDONS temp; + if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled)) + addons.insert(addons.end(), temp.begin(), temp.end()); + } + return !addons.empty(); +} + +void CAddonMgr::AddToUpdateableAddons(AddonPtr &pAddon) +{ + CSingleLock lock(m_critSection); + m_updateableAddons.push_back(pAddon); +} + +void CAddonMgr::RemoveFromUpdateableAddons(AddonPtr &pAddon) +{ + CSingleLock lock(m_critSection); + VECADDONS::iterator it = std::find(m_updateableAddons.begin(), m_updateableAddons.end(), pAddon); + + if(it != m_updateableAddons.end()) + { + m_updateableAddons.erase(it); + } +} + +struct AddonIdFinder +{ + AddonIdFinder(const std::string& id) + : m_id(id) + {} + + bool operator()(const AddonPtr& addon) + { + return m_id == addon->ID(); + } + private: + std::string m_id; +}; + +bool CAddonMgr::ReloadSettings(const std::string &id) +{ + CSingleLock lock(m_critSection); + VECADDONS::iterator it = std::find_if(m_updateableAddons.begin(), m_updateableAddons.end(), AddonIdFinder(id)); + + if( it != m_updateableAddons.end()) + { + return (*it)->ReloadSettings(); + } + return false; +} + +bool CAddonMgr::GetAllOutdatedAddons(VECADDONS &addons, bool getLocalVersion /*= false*/) +{ + CSingleLock lock(m_critSection); + for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i) + { + VECADDONS temp; + if (CAddonMgr::Get().GetAddons((TYPE)i, temp, true)) + { + AddonPtr repoAddon; + for (unsigned int j = 0; j < temp.size(); j++) + { + // Ignore duplicates due to add-ons with multiple extension points + bool found = false; + for (VECADDONS::const_iterator addonIt = addons.begin(); addonIt != addons.end(); ++addonIt) + { + if ((*addonIt)->ID() == temp[j]->ID()) + found = true; + } + + if (found || !m_database.GetAddon(temp[j]->ID(), repoAddon)) + continue; + + if (temp[j]->Version() < repoAddon->Version() && + !m_database.IsAddonBlacklisted(temp[j]->ID(), + repoAddon->Version().asString().c_str())) + { + if (getLocalVersion) + repoAddon->Props().version = temp[j]->Version(); + addons.push_back(repoAddon); + } + } + } + } + return !addons.empty(); +} + +bool CAddonMgr::HasOutdatedAddons() +{ + VECADDONS dummy; + return GetAllOutdatedAddons(dummy); +} + +bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */) +{ + CSingleLock lock(m_critSection); + addons.clear(); + if (!m_cp_context) + return false; + cp_status_t status; + int num; + std::string ext_point(TranslateType(type)); + cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num); + for(int i=0; i plugin->identifier) != enabled) + { + AddonPtr addon(Factory(props)); + if (addon) + { + if (enabled) + { + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + } + addons.push_back(addon); + } + } + } + m_cpluff->release_info(m_cp_context, exts); + return addons.size() > 0; +} + +bool CAddonMgr::GetAddon(const std::string &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/) +{ + CSingleLock lock(m_critSection); + + cp_status_t status; + cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status); + if (status == CP_OK && cpaddon) + { + addon = GetAddonFromDescriptor(cpaddon, type==ADDON_UNKNOWN?"":TranslateType(type)); + m_cpluff->release_info(m_cp_context, cpaddon); + + if (addon) + { + if (enabledOnly && IsAddonDisabled(addon->ID())) + return false; + + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + } + return NULL != addon.get(); + } + if (cpaddon) + m_cpluff->release_info(m_cp_context, cpaddon); + + return false; +} + +//TODO handle all 'default' cases here, not just scrapers & vizs +bool CAddonMgr::GetDefault(const TYPE &type, AddonPtr &addon) +{ + std::string setting; + switch (type) + { + case ADDON_VIZ: + setting = CSettings::Get().GetString("musicplayer.visualisation"); + break; + case ADDON_SCREENSAVER: + setting = CSettings::Get().GetString("screensaver.mode"); + break; + case ADDON_SCRAPER_ALBUMS: + setting = CSettings::Get().GetString("musiclibrary.albumsscraper"); + break; + case ADDON_SCRAPER_ARTISTS: + setting = CSettings::Get().GetString("musiclibrary.artistsscraper"); + break; + case ADDON_SCRAPER_MOVIES: + setting = CSettings::Get().GetString("scrapers.moviesdefault"); + break; + case ADDON_SCRAPER_MUSICVIDEOS: + setting = CSettings::Get().GetString("scrapers.musicvideosdefault"); + break; + case ADDON_SCRAPER_TVSHOWS: + setting = CSettings::Get().GetString("scrapers.tvshowsdefault"); + break; + case ADDON_WEB_INTERFACE: + setting = CSettings::Get().GetString("services.webskin"); + break; + default: + return false; + } + return GetAddon(setting, addon, type); +} + +bool CAddonMgr::SetDefault(const TYPE &type, const std::string &addonID) +{ + switch (type) + { + case ADDON_VIZ: + CSettings::Get().SetString("musicplayer.visualisation",addonID); + break; + case ADDON_SCREENSAVER: + CSettings::Get().SetString("screensaver.mode",addonID); + break; + case ADDON_SCRAPER_ALBUMS: + CSettings::Get().SetString("musiclibrary.albumsscraper",addonID); + break; + case ADDON_SCRAPER_ARTISTS: + CSettings::Get().SetString("musiclibrary.artistsscraper",addonID); + break; + case ADDON_SCRAPER_MOVIES: + CSettings::Get().SetString("scrapers.moviesdefault",addonID); + break; + case ADDON_SCRAPER_MUSICVIDEOS: + CSettings::Get().SetString("scrapers.musicvideosdefault",addonID); + break; + case ADDON_SCRAPER_TVSHOWS: + CSettings::Get().SetString("scrapers.tvshowsdefault",addonID); + break; + default: + return false; + } + + return true; +} + +std::string CAddonMgr::GetString(const std::string &id, const int number) +{ + AddonPtr addon; + if (GetAddon(id, addon)) + return addon->GetString(number); + + return ""; +} + +void CAddonMgr::FindAddons() +{ + { + CSingleLock lock(m_critSection); + if (m_cpluff && m_cp_context) + { + m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE); + SetChanged(); + } + } + NotifyObservers(ObservableMessageAddons); +} + +void CAddonMgr::RemoveAddon(const std::string& ID) +{ + if (m_cpluff && m_cp_context) + { + m_cpluff->uninstall_plugin(m_cp_context,ID.c_str()); + SetChanged(); + NotifyObservers(ObservableMessageAddons); + } +} + +bool CAddonMgr::DisableAddon(const std::string& ID, bool disable) +{ + CSingleLock lock(m_critSection); + if (m_database.DisableAddon(ID, disable)) + { + m_disabled[ID] = disable; + return true; + } + + return false; +} + +bool CAddonMgr::IsAddonDisabled(const std::string& ID) +{ + CSingleLock lock(m_critSection); + std::map::const_iterator it = m_disabled.find(ID); + if (it != m_disabled.end()) + return it->second; + + bool ret = m_database.IsAddonDisabled(ID); + m_disabled.insert(pair(ID, ret)); + + return ret; +} + +bool CAddonMgr::CanAddonBeDisabled(const std::string& ID) +{ + if (ID.empty()) + return false; + + CSingleLock lock(m_critSection); + AddonPtr localAddon; + // can't disable an addon that isn't installed + if (!IsAddonInstalled(ID, localAddon)) + return false; + + // can't disable an addon that is in use + if (localAddon->IsInUse()) + return false; + + // installed PVR addons can always be disabled + if (localAddon->Type() == ADDON_PVRDLL) + return true; + + std::string systemAddonsPath = CSpecialProtocol::TranslatePath("special://xbmc/addons"); + // can't disable system addons + if (StringUtils::StartsWith(localAddon->Path(), systemAddonsPath)) + return false; + + return true; +} + +bool CAddonMgr::IsAddonInstalled(const std::string& ID) +{ + AddonPtr tmp; + return IsAddonInstalled(ID, tmp); +} + +bool CAddonMgr::IsAddonInstalled(const std::string& ID, AddonPtr& addon) +{ + return GetAddon(ID, addon, ADDON_UNKNOWN, false); +} + +bool CAddonMgr::CanAddonBeInstalled(const std::string& ID) +{ + if (ID.empty()) + return false; + + CSingleLock lock(m_critSection); + // can't install already installed addon + if (IsAddonInstalled(ID)) + return false; + + // can't install broken addons + if (!m_database.IsAddonBroken(ID).empty()) + return false; + + return true; +} + +bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon) +{ + if (addon == NULL) + return false; + + CSingleLock lock(m_critSection); + // can't install already installed addon + if (IsAddonInstalled(addon->ID())) + return false; + + // can't install broken addons + if (!addon->Props().broken.empty()) + return false; + + return true; +} + +std::string CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag) +{ + if (!root) + return ""; + + const cp_cfg_element_t *eng = NULL; + for (unsigned int i = 0; i < root->num_children; i++) + { + const cp_cfg_element_t &child = root->children[i]; + if (strcmp(tag, child.name) == 0) + { // see if we have a "lang" attribute + const char *lang = m_cpluff->lookup_cfg_value((cp_cfg_element_t*)&child, "@lang"); + if (lang && 0 == strcmp(lang,g_langInfo.GetLanguageLocale().c_str())) + return child.value ? child.value : ""; + if (!lang || 0 == strcmp(lang, "en")) + eng = &child; + } + } + return (eng && eng->value) ? eng->value : ""; +} + +AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps) +{ + switch (addonProps.type) + { + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return AddonPtr(new CPluginSource(addonProps)); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + return AddonPtr(new CAddon(addonProps)); + case ADDON_WEB_INTERFACE: + return AddonPtr(new CWebinterface(addonProps)); + case ADDON_SERVICE: + return AddonPtr(new CService(addonProps)); + 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 AddonPtr(new CScraper(addonProps)); + case ADDON_SKIN: + return AddonPtr(new CSkinInfo(addonProps)); +#if defined(HAS_VISUALISATION) + case ADDON_VIZ: + return AddonPtr(new CVisualisation(addonProps)); +#endif + case ADDON_SCREENSAVER: + return AddonPtr(new CScreenSaver(addonProps)); + case ADDON_VIZ_LIBRARY: + return AddonPtr(new CAddonLibrary(addonProps)); + case ADDON_PVRDLL: + return AddonPtr(new PVR::CPVRClient(addonProps)); + case ADDON_AUDIOENCODER: + return AddonPtr(new CAudioEncoder(addonProps)); + case ADDON_REPOSITORY: + return AddonPtr(new CRepository(addonProps)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(addonProps)); + default: + break; + } + return AddonPtr(); +} + +/* + * libcpluff interaction + */ + +bool CAddonMgr::PlatformSupportsAddon(const cp_plugin_info_t *plugin) const +{ + if (!plugin || !plugin->num_extensions) + return false; + const cp_extension_t *metadata = GetExtension(plugin, "xbmc.addon.metadata"); // platforms; + if (CAddonMgr::Get().GetExtList(metadata->configuration, "platform", platforms)) + { + for (vector::const_iterator platform = platforms.begin(); platform != platforms.end(); ++platform) + { + if (*platform == "all") + return true; +#if defined(TARGET_ANDROID) + if (*platform == "android") +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + if (*platform == "linux") +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + if (*platform == "windx") +#elif defined(TARGET_DARWIN_OSX) +// Remove this after Frodo and add an architecture filter +// in addition to platform. +#if defined(__x86_64__) + if (*platform == "osx64" || *platform == "osx") +#else + if (*platform == "osx32" || *platform == "osx") +#endif +#elif defined(TARGET_DARWIN_IOS) + if (*platform == "ios") +#endif + return true; + } + return false; // no works for us + } + return true; // assume no is equivalent to all +} + +const cp_cfg_element_t *CAddonMgr::GetExtElement(cp_cfg_element_t *base, const char *path) +{ + const cp_cfg_element_t *element = NULL; + if (base) + element = m_cpluff->lookup_cfg_element(base, path); + return element; +} + +bool CAddonMgr::GetExtElements(cp_cfg_element_t *base, const char *path, ELEMENTS &elements) +{ + if (!base || !path) + return false; + + for (unsigned int i = 0; i < base->num_children; i++) + { + std::string temp = base->children[i].name; + if (!temp.compare(path)) + elements.push_back(&base->children[i]); + } + + return !elements.empty(); +} + +const cp_extension_t *CAddonMgr::GetExtension(const cp_plugin_info_t *props, const char *extension) const +{ + if (!props) + return NULL; + for (unsigned int i = 0; i < props->num_extensions; ++i) + { + if (0 == strcmp(props->extensions[i].ext_point_id, extension)) + return &props->extensions[i]; + } + return NULL; +} + +std::string CAddonMgr::GetExtValue(cp_cfg_element_t *base, const char *path) +{ + const char *value = ""; + if (base && (value = m_cpluff->lookup_cfg_value(base, path))) + return value; + else + return ""; +} + +bool CAddonMgr::GetExtList(cp_cfg_element_t *base, const char *path, vector &result) const +{ + result.clear(); + if (!base || !path) + return false; + const char *all = m_cpluff->lookup_cfg_value(base, path); + if (!all || *all == 0) + return false; + StringUtils::Tokenize(all, result, ' '); + return true; +} + +AddonPtr CAddonMgr::GetAddonFromDescriptor(const cp_plugin_info_t *info, + const std::string& type) +{ + if (!info) + return AddonPtr(); + + if (!info->extensions && type.empty()) + { // no extensions, so we need only the dep information + return AddonPtr(new CAddon(info)); + } + + // grab a relevant extension point, ignoring our kodi.addon.metadata extension point + for (unsigned int i = 0; i < info->num_extensions; ++i) + { + if (0 != strcmp("xbmc.addon.metadata" , info->extensions[i].ext_point_id) && //extensions[i].ext_point_id) && + (type.empty() || 0 == strcmp(type.c_str(), info->extensions[i].ext_point_id))) + { // note that Factory takes care of whether or not we have platform support + return Factory(&info->extensions[i]); + } + } + return AddonPtr(); +} + +// FIXME: This function may not be required +bool CAddonMgr::LoadAddonDescription(const std::string &path, AddonPtr &addon) +{ + cp_status_t status; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor(m_cp_context, CSpecialProtocol::TranslatePath(path).c_str(), &status); + if (info) + { + addon = GetAddonFromDescriptor(info); + m_cpluff->release_info(m_cp_context, info); + return NULL != addon.get(); + } + return false; +} + +bool CAddonMgr::AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons) +{ + // create a context for these addons + cp_status_t status; + cp_context_t *context = m_cpluff->create_context(&status); + if (!root || !context) + return false; + + // each addon XML should have a UTF-8 declaration + TiXmlDeclaration decl("1.0", "UTF-8", ""); + const TiXmlElement *element = root->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 = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); + if (info) + { + AddonPtr addon = GetAddonFromDescriptor(info); + if (addon.get()) + addons.push_back(addon); + m_cpluff->release_info(context, info); + } + element = element->NextSiblingElement("addon"); + } + m_cpluff->destroy_context(context); + return true; +} + +bool CAddonMgr::LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon) +{ + // create a context for these addons + cp_status_t status; + cp_context_t *context = m_cpluff->create_context(&status); + if (!root || !context) + return false; + + // dump the XML back to text + std::string xml; + xml << TiXmlDeclaration("1.0", "UTF-8", ""); + xml << *root; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); + if (info) + { + addon = GetAddonFromDescriptor(info); + m_cpluff->release_info(context, info); + } + m_cpluff->destroy_context(context); + return addon != NULL; +} + +bool CAddonMgr::StartServices(const bool beforelogin) +{ + CLog::Log(LOGDEBUG, "ADDON: Starting service addons."); + + VECADDONS services; + if (!GetAddons(ADDON_SERVICE, services)) + return false; + + bool ret = true; + for (IVECADDONS it = services.begin(); it != services.end(); ++it) + { + std::shared_ptr service = std::dynamic_pointer_cast(*it); + if (service) + { + if ( (beforelogin && service->GetStartOption() == CService::STARTUP) + || (!beforelogin && service->GetStartOption() == CService::LOGIN) ) + ret &= service->Start(); + } + } + + return ret; +} + +void CAddonMgr::StopServices(const bool onlylogin) +{ + CLog::Log(LOGDEBUG, "ADDON: Stopping service addons."); + + VECADDONS services; + if (!GetAddons(ADDON_SERVICE, services)) + return; + + for (IVECADDONS it = services.begin(); it != services.end(); ++it) + { + std::shared_ptr service = std::dynamic_pointer_cast(*it); + if (service) + { + if ( (onlylogin && service->GetStartOption() == CService::LOGIN) + || (!onlylogin) ) + service->Stop(); + } + } +} + +int cp_to_clog(cp_log_severity_t lvl) +{ + if (lvl >= CP_LOG_ERROR) + return LOGINFO; + return LOGDEBUG; +} + +cp_log_severity_t clog_to_cp(int lvl) +{ + if (lvl >= LOG_LEVEL_DEBUG) + return CP_LOG_INFO; + return CP_LOG_ERROR; +} + +void cp_fatalErrorHandler(const char *msg) +{ + CLog::Log(LOGERROR, "ADDONS: CPluffFatalError(%s)", msg); +} + +void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data) +{ + if(!apid) + CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s'", msg); + else + CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s' reports '%s'", apid, msg); +} + +} /* namespace ADDON */ + diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h new file mode 100644 index 0000000..8bc058d --- /dev/null +++ b/xbmc/addons/AddonManager.h @@ -0,0 +1,253 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "Addon.h" +#include "threads/CriticalSection.h" +#include "utils/Observer.h" +#include +#include +#include +#include +#include "AddonDatabase.h" + +class DllLibCPluff; +extern "C" +{ +#include "lib/cpluff/libcpluff/cpluff.h" +} + +namespace ADDON +{ + typedef std::map MAPADDONS; + typedef std::map::iterator IMAPADDONS; + typedef std::vector ELEMENTS; + + const std::string ADDON_METAFILE = "description.xml"; + const std::string ADDON_VIS_EXT = "*.vis"; + const std::string ADDON_PYTHON_EXT = "*.py"; + const std::string ADDON_SCRAPER_EXT = "*.xml"; + const std::string ADDON_SCREENSAVER_EXT = "*.xbs"; + const std::string ADDON_PVRDLL_EXT = "*.pvr"; + const std::string ADDON_DSP_AUDIO_EXT = "*.adsp"; + const std::string ADDON_VERSION_RE = "(?\\d*)\\.?(?\\d*)?\\.?(?\\d*)?\\.?(?\\d*)?"; + + /** + * Class - IAddonMgrCallback + * This callback should be inherited by any class which manages + * specific addon types. Could be mostly used for Dll addon types to handle + * cleanup before restart/removal + */ + class IAddonMgrCallback + { + public: + virtual ~IAddonMgrCallback() {}; + virtual bool RequestRestart(AddonPtr addon, bool datachanged)=0; + virtual bool RequestRemoval(AddonPtr addon)=0; + }; + + /** + * Class - CAddonMgr + * Holds references to all addons, enabled or + * otherwise. Services the generic callbacks available + * to all addon variants. + */ + class CAddonMgr : public Observable + { + public: + static CAddonMgr &Get(); + bool ReInit() { DeInit(); return Init(); } + bool Init(); + void DeInit(); + + IAddonMgrCallback* GetCallbackForType(TYPE type); + bool RegisterAddonMgrCallback(TYPE type, IAddonMgrCallback* cb); + void UnregisterAddonMgrCallback(TYPE type); + + /* Addon access */ + bool GetDefault(const TYPE &type, AddonPtr &addon); + bool SetDefault(const TYPE &type, const std::string &addonID); + /*! \brief Retrieve a specific addon (of a specific type) + \param id the id of the addon to retrieve. + \param addon [out] the retrieved addon pointer - only use if the function returns true. + \param type type of addon to retrieve - defaults to any type. + \param enabledOnly whether we only want enabled addons - set to false to allow both enabled and disabled addons - defaults to true. + \return true if an addon matching the id of the given type is available and is enabled (if enabledOnly is true). + */ + bool GetAddon(const std::string &id, AddonPtr &addon, const TYPE &type = ADDON_UNKNOWN, bool enabledOnly = true); + bool HasAddons(const TYPE &type, bool enabled = true); + bool GetAddons(const TYPE &type, VECADDONS &addons, bool enabled = true); + bool GetAllAddons(VECADDONS &addons, bool enabled = true, bool allowRepos = false); + void AddToUpdateableAddons(AddonPtr &pAddon); + void RemoveFromUpdateableAddons(AddonPtr &pAddon); + bool ReloadSettings(const std::string &id); + /*! \brief Get all addons with available updates + \param addons List to fill with all outdated addons + \param getLocalVersion Whether to get the local addon version or the addon verion from the repository + \return True if there are outdated addons otherwise false + */ + bool GetAllOutdatedAddons(VECADDONS &addons, bool getLocalVersion = false); + /*! \brief Checks if there is any addon with available updates + \return True if there are outdated addons otherwise false + */ + bool HasOutdatedAddons(); + std::string GetString(const std::string &id, const int number); + + std::string GetTranslatedString(const cp_cfg_element_t *root, const char *tag); + static AddonPtr AddonFromProps(AddonProps& props); + void FindAddons(); + void RemoveAddon(const std::string& ID); + + /* \brief Disable an addon + Triggers the database routine and saves the current addon state to cache. + \param ID id of the addon + \param disable whether to enable or disable. Defaults to true (disable) + \sa IsAddonDisabled, + */ + bool DisableAddon(const std::string& ID, bool disable = true); + + /* \brief Check whether an addon has been disabled via DisableAddon. + In case the disabled cache does not know about the current state the database routine will be used. + \param ID id of the addon + \sa DisableAddon + */ + bool IsAddonDisabled(const std::string& ID); + + /* \brief Checks whether an addon can be disabled via DisableAddon. + \param ID id of the addon + \sa DisableAddon + */ + bool CanAddonBeDisabled(const std::string& ID); + + /* \brief Checks whether an addon is installed. + \param ID id of the addon + */ + bool IsAddonInstalled(const std::string& ID); + + /* \brief Checks whether an addon is installed. + \param ID id of the addon + \param addon Installed addon + */ + bool IsAddonInstalled(const std::string& ID, AddonPtr& addon); + + /* \brief Checks whether an addon can be installed. Broken addons can't be installed. + \param ID id of the addon + */ + bool CanAddonBeInstalled(const std::string& ID); + + /* \brief Checks whether an addon can be installed. Broken addons can't be installed. + \param addon addon to be checked + */ + bool CanAddonBeInstalled(const AddonPtr& addon); + + /* libcpluff */ + std::string GetExtValue(cp_cfg_element_t *base, const char *path); + + /*! \brief Retrieve a vector of repeated elements from a given configuration element + \param base the base configuration element. + \param path the path to the configuration element from the base element. + \param result [out] returned list of elements. + \return true if the configuration element is present and the list of elements is non-empty + */ + bool GetExtElements(cp_cfg_element_t *base, const char *path, ELEMENTS &result); + + /*! \brief Retrieve a list of strings from a given configuration element + Assumes the configuration element or attribute contains a whitespace separated list of values (eg xs:list schema). + \param base the base configuration element. + \param path the path to the configuration element or attribute from the base element. + \param result [out] returned list of strings. + \return true if the configuration element is present and the list of strings is non-empty + */ + bool GetExtList(cp_cfg_element_t *base, const char *path, std::vector &result) const; + + const cp_extension_t *GetExtension(const cp_plugin_info_t *props, const char *extension) const; + + /*! \brief Load the addon in the given path + This loads the addon using c-pluff which parses the addon descriptor file. + \param path folder that contains the addon. + \param addon [out] returned addon. + \return true if addon is set, false otherwise. + */ + bool LoadAddonDescription(const std::string &path, AddonPtr &addon); + + /*! \brief Load the addon in the given in-memory xml + This loads the addon using c-pluff which parses the addon descriptor file. + \param root Root element of an XML document. + \param addon [out] returned addon. + \return true if addon is set, false otherwise. + */ + bool LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon); + + /*! \brief Parse a repository XML file for addons and load their descriptors + A repository XML is essentially a concatenated list of addon descriptors. + \param root Root element of an XML document. + \param addons [out] returned list of addons. + \return true if the repository XML file is parsed, false otherwise. + */ + bool AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons); + + /*! \brief Start all services addons. + \return True is all addons are started, false otherwise + */ + bool StartServices(const bool beforelogin); + /*! \brief Stop all services addons. + */ + void StopServices(const bool onlylogin); + + private: + void LoadAddons(const std::string &path, + std::map& unresolved); + + /* libcpluff */ + const cp_cfg_element_t *GetExtElement(cp_cfg_element_t *base, const char *path); + cp_context_t *m_cp_context; + DllLibCPluff *m_cpluff; + VECADDONS m_updateableAddons; + + /*! \brief Fetch a (single) addon from a plugin descriptor. + Assumes that there is a single (non-trivial) extension point per addon. + \param info the plugin descriptor + \param type the extension point we want + \return an AddonPtr based on the descriptor. May be NULL if no suitable extension point is found. + */ + AddonPtr GetAddonFromDescriptor(const cp_plugin_info_t *info, + const std::string& type=""); + + /*! \brief Check whether this addon is supported on the current platform + \param info the plugin descriptor + \return true if the addon is supported, false otherwise. + */ + bool PlatformSupportsAddon(const cp_plugin_info_t *info) const; + + AddonPtr Factory(const cp_extension_t *props); + bool CheckUserDirs(const cp_cfg_element_t *element); + + // private construction, and no assignements; use the provided singleton methods + CAddonMgr(); + CAddonMgr(const CAddonMgr&); + CAddonMgr const& operator=(CAddonMgr const&); + virtual ~CAddonMgr(); + + std::map m_disabled; + static std::map m_managers; + CCriticalSection m_critSection; + CAddonDatabase m_database; + }; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonStatusHandler.cpp b/xbmc/addons/AddonStatusHandler.cpp new file mode 100644 index 0000000..7bb874a --- /dev/null +++ b/xbmc/addons/AddonStatusHandler.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "AddonStatusHandler.h" +#include "AddonManager.h" +#include "threads/SingleLock.h" +#include "ApplicationMessenger.h" +#include "guilib/GUIWindowManager.h" +#include "GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/StringUtils.h" + +namespace ADDON +{ + +/********************************************************** + * CAddonStatusHandler - AddOn Status Report Class + * + * Used to informate the user about occurred errors and + * changes inside Add-on's, and ask him what to do. + * + */ + +CCriticalSection CAddonStatusHandler::m_critSection; + +CAddonStatusHandler::CAddonStatusHandler(const std::string &addonID, ADDON_STATUS status, std::string message, bool sameThread) + : CThread(("AddonStatus " + addonID).c_str()) +{ + if (!CAddonMgr::Get().GetAddon(addonID, m_addon)) + return; + + CLog::Log(LOGINFO, "Called Add-on status handler for '%u' of clientName:%s, clientID:%s (same Thread=%s)", status, m_addon->Name().c_str(), m_addon->ID().c_str(), sameThread ? "yes" : "no"); + + m_status = status; + m_message = message; + + if (sameThread) + { + Process(); + } + else + { + Create(true, THREAD_MINSTACKSIZE); + } +} + +CAddonStatusHandler::~CAddonStatusHandler() +{ + StopThread(); +} + +void CAddonStatusHandler::OnStartup() +{ + SetPriority(GetMinPriority()); +} + +void CAddonStatusHandler::OnExit() +{ +} + +void CAddonStatusHandler::Process() +{ + CSingleLock lock(m_critSection); + + std::string heading = StringUtils::Format("%s: %s", TranslateType(m_addon->Type(), true).c_str(), m_addon->Name().c_str()); + + /* AddOn lost connection to his backend (for ones that use Network) */ + if (m_status == ADDON_STATUS_LOST_CONNECTION) + { + if (m_addon->Type() == ADDON_PVRDLL) + { + if (!CSettings::Get().GetBool("pvrmanager.hideconnectionlostwarning")) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, m_addon->Name().c_str(), g_localizeStrings.Get(36030)); // connection lost + // TODO handle disconnects after the add-on's been initialised + } + else + { + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) return; + + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24070); + pDialog->SetLine(2, 24073); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + + if (pDialog->IsConfirmed()) + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, false); + } + } + /* Request to restart the AddOn and data structures need updated */ + else if (m_status == ADDON_STATUS_NEED_RESTART) + { + CGUIDialogOK* pDialog = (CGUIDialogOK*)g_windowManager.GetWindow(WINDOW_DIALOG_OK); + if (!pDialog) return; + + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24074); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_OK, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, true); + } + /* Some required settings are missing/invalid */ + else if ((m_status == ADDON_STATUS_NEED_SETTINGS) || (m_status == ADDON_STATUS_NEED_SAVEDSETTINGS)) + { + CGUIDialogYesNo* pDialogYesNo = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialogYesNo) return; + + pDialogYesNo->SetHeading(heading); + pDialogYesNo->SetLine(1, 24070); + pDialogYesNo->SetLine(2, 24072); + pDialogYesNo->SetLine(3, m_message); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + + if (!pDialogYesNo->IsConfirmed()) return; + + if (!m_addon->HasSettings()) + return; + + if (CGUIDialogAddonSettings::ShowAndGetInput(m_addon)) + { + //todo doesn't dialogaddonsettings save these automatically? should do + m_addon->SaveSettings(); + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, true); + } + } + /* A unknown event has occurred */ + else if (m_status == ADDON_STATUS_UNKNOWN) + { + //CAddonMgr::Get().DisableAddon(m_addon->ID()); + CGUIDialogOK* pDialog = (CGUIDialogOK*)g_windowManager.GetWindow(WINDOW_DIALOG_OK); + if (!pDialog) return; + + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24070); + pDialog->SetLine(2, 24071); + pDialog->SetLine(3, m_message); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_OK, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + } +} + + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/AddonStatusHandler.h b/xbmc/addons/AddonStatusHandler.h new file mode 100644 index 0000000..c9b65bd --- /dev/null +++ b/xbmc/addons/AddonStatusHandler.h @@ -0,0 +1,56 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "threads/Thread.h" +#include "IAddon.h" +#include "include/xbmc_addon_types.h" +#include "threads/CriticalSection.h" +#include + +namespace ADDON +{ + /** + * Class - CAddonStatusHandler + * Used to informate the user about occurred errors and + * changes inside Add-on's, and ask him what to do. + * It can executed in the same thread as the calling + * function or in a seperate thread. + */ + class CAddonStatusHandler : private CThread + { + public: + CAddonStatusHandler(const std::string &addonID, ADDON_STATUS status, std::string message, bool sameThread = true); + ~CAddonStatusHandler(); + + /* Thread handling */ + virtual void Process(); + virtual void OnStartup(); + virtual void OnExit(); + + private: + static CCriticalSection m_critSection; + AddonPtr m_addon; + ADDON_STATUS m_status; + std::string m_message; + }; + + +} diff --git a/xbmc/addons/AddonVersion.cpp b/xbmc/addons/AddonVersion.cpp new file mode 100644 index 0000000..9d5a112 --- /dev/null +++ b/xbmc/addons/AddonVersion.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include +#include + +#include "AddonVersion.h" +#include "utils/StringUtils.h" + +namespace ADDON +{ + AddonVersion::AddonVersion(const std::string& version) + : mEpoch(0), mUpstream(version.empty() ? "0.0.0" : version) + { + size_t pos = mUpstream.find(':'); + if (pos != std::string::npos) + { + mEpoch = strtol(mUpstream.c_str(), NULL, 10); + mUpstream.erase(0, pos+1); + } + + pos = mUpstream.find('-'); + if (pos != std::string::npos) + { + mRevision = mUpstream.substr(pos+1); + mUpstream.erase(pos); + } + } + + /**Compare two components of a Debian-style version. Return -1, 0, or 1 + * if a is less than, equal to, or greater than b, respectively. + */ + int AddonVersion::CompareComponent(const char *a, const char *b) + { + while (*a && *b) + { + while (*a && *b && !isdigit(*a) && !isdigit(*b)) + { + if (*a != *b) + { + if (*a == '~') return -1; + if (*b == '~') return 1; + return *a < *b ? -1 : 1; + } + a++; + b++; + } + if (*a && *b && (!isdigit(*a) || !isdigit(*b))) + { + if (*a == '~') return -1; + if (*b == '~') return 1; + return isdigit(*a) ? -1 : 1; + } + + char *next_a, *next_b; + long int num_a = strtol(a, &next_a, 10); + long int num_b = strtol(b, &next_b, 10); + if (num_a != num_b) + return num_a < num_b ? -1 : 1; + + a = next_a; + b = next_b; + } + if (!*a && !*b) + return 0; + if (*a) + return *a == '~' ? -1 : 1; + else + return *b == '~' ? 1 : -1; + } + + bool AddonVersion::operator<(const AddonVersion& other) const + { + if (mEpoch != other.mEpoch) + return mEpoch < other.mEpoch; + + int result = CompareComponent(mUpstream.c_str(), other.mUpstream.c_str()); + if (result) + return (result < 0); + + return (CompareComponent(mRevision.c_str(), other.mRevision.c_str()) < 0); + } + + bool AddonVersion::operator==(const AddonVersion& other) const + { + return mEpoch == other.mEpoch + && CompareComponent(mUpstream.c_str(), other.mUpstream.c_str()) == 0 + && CompareComponent(mRevision.c_str(), other.mRevision.c_str()) == 0; + } + + bool AddonVersion::empty() const + { + return mEpoch == 0 && mUpstream == "0.0.0" && mRevision.empty(); + } + + std::string AddonVersion::asString() const + { + std::string out; + if (mEpoch) + out = StringUtils::Format("%i:", mEpoch); + out += mUpstream; + if (!mRevision.empty()) + out += "-" + mRevision; + return out; + } + + bool AddonVersion::SplitFileName(std::string& ID, std::string& version, + const std::string& filename) + { + size_t dpos = filename.rfind("-"); + if (dpos == std::string::npos) + return false; + ID = filename.substr(0, dpos); + version = filename.substr(dpos + 1); + version = version.substr(0, version.size() - 4); + + return true; + } +} diff --git a/xbmc/addons/AddonVersion.h b/xbmc/addons/AddonVersion.h new file mode 100644 index 0000000..3c23370 --- /dev/null +++ b/xbmc/addons/AddonVersion.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include + +namespace ADDON +{ + /* \brief Addon versioning using the debian versioning scheme + + AddonVersion uses debian versioning, which means in the each section of the period + separated version string, numbers are compared numerically rather than lexicographically, + thus any preceding zeros are ignored. + + i.e. 1.00 is considered the same as 1.0, and 1.01 is considered the same as 1.1. + + Further, 1.0 < 1.0.0 + + See here for more info: http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version + */ + class AddonVersion : public boost::totally_ordered { + public: + AddonVersion(const AddonVersion& other) { *this = other; } + explicit AddonVersion(const std::string& version); + virtual ~AddonVersion() {}; + + int Epoch() const { return mEpoch; } + const std::string &Upstream() const { return mUpstream; } + const std::string &Revision() const { return mRevision; } + + AddonVersion& operator=(const AddonVersion& other); + bool operator<(const AddonVersion& other) const; + bool operator==(const AddonVersion& other) const; + std::string asString() const; + bool empty() const; + + static bool SplitFileName(std::string& ID, std::string& version, + const std::string& filename); + + protected: + int mEpoch; + std::string mUpstream; + std::string mRevision; + + static int CompareComponent(const char *a, const char *b); + }; + + inline AddonVersion& AddonVersion::operator=(const AddonVersion& other) + { + mEpoch = other.mEpoch; + mUpstream = other.mUpstream; + mRevision = other.mRevision; + return *this; + } +} diff --git a/xbmc/addons/AudioEncoder.cpp b/xbmc/addons/AudioEncoder.cpp new file mode 100644 index 0000000..2737bba --- /dev/null +++ b/xbmc/addons/AudioEncoder.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Arne Morten Kvarving + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "AudioEncoder.h" + +namespace ADDON +{ + +CAudioEncoder::CAudioEncoder(const cp_extension_t* ext) + : AudioEncoderDll(ext), + extension(CAddonMgr::Get().GetExtValue(ext->configuration, "@extension")), + m_context(NULL) +{ +} + +AddonPtr CAudioEncoder::Clone() const +{ + // Copy constructor is generated by compiler and calls parent copy constructor + return AddonPtr(new CAudioEncoder(*this)); +} + +bool CAudioEncoder::Init(audioenc_callbacks &callbacks) +{ + if (!Initialized()) + return false; + + // create encoder instance + m_context = m_pStruct->Create(&callbacks); + if (!m_context) + return false; + + return m_pStruct->Start(m_context, + m_iInChannels, + m_iInSampleRate, + m_iInBitsPerSample, + m_strTitle.c_str(), + m_strArtist.c_str(), + m_strAlbumArtist.c_str(), + m_strAlbum.c_str(), + m_strYear.c_str(), + m_strTrack.c_str(), + m_strGenre.c_str(), + m_strComment.c_str(), + m_iTrackLength); +} + +int CAudioEncoder::Encode(int nNumBytesRead, uint8_t* pbtStream) +{ + if (!Initialized() || !m_context) + return 0; + + return m_pStruct->Encode(m_context, nNumBytesRead, pbtStream); +} + +bool CAudioEncoder::Close() +{ + if (!Initialized() || !m_context) + return false; + + if (!m_pStruct->Finish(m_context)) + return false; + + m_pStruct->Free(m_context); + m_context = NULL; + + return true; +} + +void CAudioEncoder::Destroy() +{ + AudioEncoderDll::Destroy(); +} + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/AudioEncoder.h b/xbmc/addons/AudioEncoder.h new file mode 100644 index 0000000..3900333 --- /dev/null +++ b/xbmc/addons/AudioEncoder.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Arne Morten Kvarving + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "AddonDll.h" +#include "include/xbmc_audioenc_types.h" +#include "cdrip/IEncoder.h" + +typedef DllAddon DllAudioEncoder; +namespace ADDON +{ + typedef CAddonDll AudioEncoderDll; + + class CAudioEncoder : public AudioEncoderDll, public IEncoder + { + public: + CAudioEncoder(const AddonProps &props) : AudioEncoderDll(props) {}; + CAudioEncoder(const cp_extension_t *ext); + virtual ~CAudioEncoder() {} + virtual AddonPtr Clone() const; + + // Things that MUST be supplied by the child classes + bool Init(audioenc_callbacks &callbacks); + int Encode(int nNumBytesRead, uint8_t* pbtStream); + bool Close(); + void Destroy(); + + const std::string extension; + + private: + void *m_context; ///< audio encoder context + }; + +} /*namespace ADDON*/ diff --git a/xbmc/addons/ContextItemAddon.cpp b/xbmc/addons/ContextItemAddon.cpp new file mode 100644 index 0000000..4222936 --- /dev/null +++ b/xbmc/addons/ContextItemAddon.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013-2015 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "ContextItemAddon.h" +#include "AddonManager.h" +#include "ContextMenuManager.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "GUIInfoManager.h" +#include "interfaces/info/InfoBool.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "video/dialogs/GUIDialogVideoInfo.h" +#include + +using namespace std; + +namespace ADDON +{ + +CContextItemAddon::CContextItemAddon(const AddonProps &props) + : CAddon(props) +{ } + +CContextItemAddon::~CContextItemAddon() +{ } + +CContextItemAddon::CContextItemAddon(const cp_extension_t *ext) + : CAddon(ext) +{ + ELEMENTS items; + if (CAddonMgr::Get().GetExtElements(ext->configuration, "item", items)) + { + cp_cfg_element_t *item = items[0]; + + m_label = CAddonMgr::Get().GetExtValue(item, "label"); + if (StringUtils::IsNaturalNumber(m_label)) + { + m_label = GetString(boost::lexical_cast(m_label.c_str())); + ClearStrings(); + } + + m_parent = CAddonMgr::Get().GetExtValue(item, "parent"); + + string visible = CAddonMgr::Get().GetExtValue(item, "visible"); + if (visible.empty()) + visible = "false"; + + m_visCondition = g_infoManager.Register(visible, 0); + } +} + +bool CContextItemAddon::OnPreInstall() +{ + return CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} + +void CContextItemAddon::OnPostInstall(bool restart, bool update) +{ + if (restart) + { + // need to grab the local addon so we have the correct library path to run + AddonPtr localAddon; + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_CONTEXT_ITEM)) + { + ContextItemAddonPtr contextItem = std::dynamic_pointer_cast(localAddon); + if (contextItem) + CContextMenuManager::Get().Register(contextItem); + } + } +} + +void CContextItemAddon::OnPreUnInstall() +{ + CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} + +void CContextItemAddon::OnDisabled() +{ + CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} +void CContextItemAddon::OnEnabled() +{ + CContextMenuManager::Get().Register(std::dynamic_pointer_cast(shared_from_this())); +} + +bool CContextItemAddon::IsVisible(const CFileItemPtr& item) const +{ + return item && m_visCondition->Get(item.get()); +} + +} diff --git a/xbmc/addons/ContextItemAddon.h b/xbmc/addons/ContextItemAddon.h new file mode 100644 index 0000000..628fdcd --- /dev/null +++ b/xbmc/addons/ContextItemAddon.h @@ -0,0 +1,72 @@ +#pragma once +/* + * Copyright (C) 2013-2015 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include +#include "Addon.h" + +class CFileItem; +typedef std::shared_ptr CFileItemPtr; + +namespace INFO +{ + class InfoBool; + typedef std::shared_ptr InfoPtr; +} + +namespace ADDON +{ + class CContextItemAddon : public CAddon + { + public: + CContextItemAddon(const cp_extension_t *ext); + CContextItemAddon(const AddonProps &props); + virtual ~CContextItemAddon(); + + const std::string& GetLabel() const { return m_label; } + + /*! + * \brief Get the parent category of this context item. + * + * \details Returns empty string if at root level or + * CONTEXT_MENU_GROUP_MANAGE when it should be in the 'manage' submenu. + */ + const std::string& GetParent() const { return m_parent; } + + /*! + * \brief Returns true if this contex menu should be visible for given item. + */ + bool IsVisible(const CFileItemPtr& item) const; + + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update); + virtual void OnPreUnInstall(); + virtual void OnDisabled(); + virtual void OnEnabled(); + + private: + std::string m_label; + std::string m_parent; + INFO::InfoPtr m_visCondition; + }; + + typedef std::shared_ptr ContextItemAddonPtr; +} diff --git a/xbmc/addons/DllAddon.h b/xbmc/addons/DllAddon.h new file mode 100644 index 0000000..3ea4e8d --- /dev/null +++ b/xbmc/addons/DllAddon.h @@ -0,0 +1,70 @@ +#pragma once +/* +* Copyright (C) 2005-2013 Team XBMC +* http://xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, see +* . +* +*/ + +#include "DynamicDll.h" +#include "addons/include/xbmc_addon_cpp_dll.h" + +template +class DllAddonInterface +{ +public: + virtual ~DllAddonInterface() {} + virtual void GetAddon(TheStruct* pAddon) =0; + virtual ADDON_STATUS Create(void *cb, Props *info) =0; + virtual void Stop() =0; + virtual void Destroy() =0; + virtual ADDON_STATUS GetStatus() =0; + virtual bool HasSettings() =0; + virtual unsigned int GetSettings(ADDON_StructSetting*** sSet)=0; + virtual void FreeSettings()=0; + virtual ADDON_STATUS SetSetting(const char *settingName, const void *settingValue) =0; + virtual void Announce(const char *flag, const char *sender, const char *message, const void *data) =0; +}; + +template +class DllAddon : public DllDynamic, public DllAddonInterface +{ +public: + DECLARE_DLL_WRAPPER_TEMPLATE(DllAddon) + DEFINE_METHOD2(ADDON_STATUS, Create, (void* p1, Props* p2)) + DEFINE_METHOD0(void, Stop) + DEFINE_METHOD0(void, Destroy) + DEFINE_METHOD0(ADDON_STATUS, GetStatus) + DEFINE_METHOD0(bool, HasSettings) + DEFINE_METHOD1(unsigned int, GetSettings, (ADDON_StructSetting ***p1)) + DEFINE_METHOD0(void, FreeSettings) + DEFINE_METHOD2(ADDON_STATUS, SetSetting, (const char *p1, const void *p2)) + DEFINE_METHOD1(void, GetAddon, (TheStruct* p1)) + DEFINE_METHOD4(void, Announce, (const char *p1, const char *p2, const char *p3, const void *p4)) + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(get_addon,GetAddon) + RESOLVE_METHOD_RENAME(ADDON_Create, Create) + RESOLVE_METHOD_RENAME(ADDON_Stop, Stop) + RESOLVE_METHOD_RENAME(ADDON_Destroy, Destroy) + RESOLVE_METHOD_RENAME(ADDON_GetStatus, GetStatus) + RESOLVE_METHOD_RENAME(ADDON_HasSettings, HasSettings) + RESOLVE_METHOD_RENAME(ADDON_SetSetting, SetSetting) + RESOLVE_METHOD_RENAME(ADDON_GetSettings, GetSettings) + RESOLVE_METHOD_RENAME(ADDON_FreeSettings, FreeSettings) + RESOLVE_METHOD_RENAME(ADDON_Announce, Announce) + END_METHOD_RESOLVE() +}; + diff --git a/xbmc/addons/DllLibCPluff.h b/xbmc/addons/DllLibCPluff.h new file mode 100644 index 0000000..6e3f03c --- /dev/null +++ b/xbmc/addons/DllLibCPluff.h @@ -0,0 +1,116 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DynamicDll.h" + +extern "C" { +#include "lib/cpluff/libcpluff/cpluff.h" +} + +class DllLibCPluffInterface +{ +public: + virtual ~DllLibCPluffInterface() {} + virtual const char *get_version(void) =0; + virtual void set_fatal_error_handler(cp_fatal_error_func_t error_handler) =0; + virtual cp_status_t init(void) =0; + virtual void destroy(void) =0; + virtual cp_context_t * create_context(cp_status_t *status) =0; + virtual void destroy_context(cp_context_t *ctx) =0; + virtual cp_status_t register_pcollection(cp_context_t *ctx, const char *dir) =0; + virtual void unregister_pcollection(cp_context_t *ctx, const char *dir) =0; + virtual void unregister_pcollections(cp_context_t *ctx) =0; + virtual cp_status_t register_logger(cp_context_t *ctx, cp_logger_func_t logger, void *user_data, cp_log_severity_t min_severity) =0; + virtual void unregister_logger(cp_context_t *ctx, cp_logger_func_t logger) =0; + virtual cp_status_t scan_plugins(cp_context_t *ctx, int flags) =0; + virtual cp_plugin_info_t * get_plugin_info(cp_context_t *ctx, const char *id, cp_status_t *status) =0; + virtual cp_plugin_info_t ** get_plugins_info(cp_context_t *ctx, cp_status_t *status, int *num) =0; + virtual cp_extension_t ** get_extensions_info(cp_context_t *ctx, const char *extpt_id, cp_status_t *status, int *num) =0; + virtual void release_info(cp_context_t *ctx, void *info) =0; + virtual cp_cfg_element_t * lookup_cfg_element(cp_cfg_element_t *base, const char *path) =0; + virtual char * lookup_cfg_value(cp_cfg_element_t *base, const char *path) =0; + virtual cp_status_t define_symbol(cp_context_t *ctx, const char *name, void *ptr) =0; + virtual void *resolve_symbol(cp_context_t *ctx, const char *id, const char *name, cp_status_t *status) =0; + virtual void release_symbol(cp_context_t *ctx, const void *ptr) =0; + virtual cp_plugin_info_t *load_plugin_descriptor(cp_context_t *ctx, const char *path, cp_status_t *status) =0; + virtual cp_plugin_info_t *load_plugin_descriptor_from_memory(cp_context_t *ctx, const char *buffer, unsigned int buffer_len, cp_status_t *status) =0; + virtual cp_status_t uninstall_plugin(cp_context_t *ctx, const char *id)=0; +}; + +class DllLibCPluff : public DllDynamic, DllLibCPluffInterface +{ + DECLARE_DLL_WRAPPER(DllLibCPluff, DLL_PATH_CPLUFF) + DEFINE_METHOD0(const char*, get_version) + DEFINE_METHOD1(void, set_fatal_error_handler, (cp_fatal_error_func_t p1)) + DEFINE_METHOD0(cp_status_t, init) + DEFINE_METHOD0(void, destroy) + DEFINE_METHOD1(cp_context_t*, create_context, (cp_status_t *p1)) + DEFINE_METHOD1(void, destroy_context, (cp_context_t *p1)) + + DEFINE_METHOD2(cp_status_t, register_pcollection, (cp_context_t *p1, const char *p2)) + DEFINE_METHOD2(void, unregister_pcollection, (cp_context_t *p1, const char *p2)) + DEFINE_METHOD1(void, unregister_pcollections, (cp_context_t *p1)) + + DEFINE_METHOD4(cp_status_t, register_logger, (cp_context_t *p1, cp_logger_func_t p2, void *p3, cp_log_severity_t p4)) + DEFINE_METHOD2(void, unregister_logger, (cp_context_t *p1, cp_logger_func_t p2)) + DEFINE_METHOD2(cp_status_t, scan_plugins, (cp_context_t *p1, int p2)) + DEFINE_METHOD3(cp_plugin_info_t*, get_plugin_info, (cp_context_t *p1, const char *p2, cp_status_t *p3)) + DEFINE_METHOD3(cp_plugin_info_t**, get_plugins_info, (cp_context_t *p1, cp_status_t *p2, int *p3)) + DEFINE_METHOD4(cp_extension_t**, get_extensions_info, (cp_context_t *p1, const char *p2, cp_status_t *p3, int *p4)) + DEFINE_METHOD2(void, release_info, (cp_context_t *p1, void *p2)) + + DEFINE_METHOD2(cp_cfg_element_t*, lookup_cfg_element, (cp_cfg_element_t *p1, const char *p2)) + DEFINE_METHOD2(char*, lookup_cfg_value, (cp_cfg_element_t *p1, const char *p2)) + + DEFINE_METHOD3(cp_status_t, define_symbol, (cp_context_t *p1, const char *p2, void *p3)) + DEFINE_METHOD4(void*, resolve_symbol, (cp_context_t *p1, const char *p2, const char *p3, cp_status_t *p4)) + DEFINE_METHOD2(void, release_symbol, (cp_context_t *p1, const void *p2)) + DEFINE_METHOD3(cp_plugin_info_t*, load_plugin_descriptor, (cp_context_t *p1, const char *p2, cp_status_t *p3)) + DEFINE_METHOD4(cp_plugin_info_t*, load_plugin_descriptor_from_memory, (cp_context_t *p1, const char *p2, unsigned int p3, cp_status_t *p4)) + DEFINE_METHOD2(cp_status_t, uninstall_plugin, (cp_context_t *p1, const char *p2)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(cp_get_version, get_version) + RESOLVE_METHOD_RENAME(cp_set_fatal_error_handler, set_fatal_error_handler) + RESOLVE_METHOD_RENAME(cp_init, init) + RESOLVE_METHOD_RENAME(cp_destroy, destroy) + RESOLVE_METHOD_RENAME(cp_create_context, create_context) + RESOLVE_METHOD_RENAME(cp_destroy_context, destroy_context) + RESOLVE_METHOD_RENAME(cp_register_pcollection, register_pcollection) + RESOLVE_METHOD_RENAME(cp_unregister_pcollection, unregister_pcollection) + RESOLVE_METHOD_RENAME(cp_unregister_pcollections, unregister_pcollections) + RESOLVE_METHOD_RENAME(cp_register_logger, register_logger) + RESOLVE_METHOD_RENAME(cp_unregister_logger, unregister_logger) + RESOLVE_METHOD_RENAME(cp_scan_plugins, scan_plugins) + RESOLVE_METHOD_RENAME(cp_get_plugin_info, get_plugin_info) + RESOLVE_METHOD_RENAME(cp_get_plugins_info, get_plugins_info) + RESOLVE_METHOD_RENAME(cp_get_extensions_info, get_extensions_info) + RESOLVE_METHOD_RENAME(cp_release_info, release_info) + RESOLVE_METHOD_RENAME(cp_lookup_cfg_element, lookup_cfg_element) + RESOLVE_METHOD_RENAME(cp_lookup_cfg_value, lookup_cfg_value) + RESOLVE_METHOD_RENAME(cp_define_symbol, define_symbol) + RESOLVE_METHOD_RENAME(cp_resolve_symbol, resolve_symbol) + RESOLVE_METHOD_RENAME(cp_release_symbol, release_symbol) + RESOLVE_METHOD_RENAME(cp_load_plugin_descriptor, load_plugin_descriptor) + RESOLVE_METHOD_RENAME(cp_load_plugin_descriptor_from_memory, load_plugin_descriptor_from_memory) + RESOLVE_METHOD_RENAME(cp_uninstall_plugin, uninstall_plugin) + END_METHOD_RESOLVE() +}; diff --git a/xbmc/addons/DllPVRClient.h b/xbmc/addons/DllPVRClient.h new file mode 100644 index 0000000..7948858 --- /dev/null +++ b/xbmc/addons/DllPVRClient.h @@ -0,0 +1,29 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DllAddon.h" +#include "include/xbmc_pvr_types.h" + +class DllPVRClient : public DllAddon +{ + // this is populated via Macro calls in DllAddon.h +}; + diff --git a/xbmc/addons/GUIDialogAddonInfo.cpp b/xbmc/addons/GUIDialogAddonInfo.cpp new file mode 100644 index 0000000..fe3484f --- /dev/null +++ b/xbmc/addons/GUIDialogAddonInfo.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "GUIDialogAddonInfo.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "addons/AddonManager.h" +#include "AddonDatabase.h" +#include "FileItem.h" +#include "filesystem/Directory.h" +#include "filesystem/SpecialProtocol.h" +#include "GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "GUIUserMessages.h" +#include "guilib/GUIWindowManager.h" +#include "input/Key.h" +#include "utils/JobManager.h" +#include "utils/FileOperationJob.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "addons/AddonInstaller.h" +#include "pvr/PVRManager.h" +#include "Util.h" +#include "interfaces/Builtins.h" + +#define CONTROL_BTN_INSTALL 6 +#define CONTROL_BTN_ENABLE 7 +#define CONTROL_BTN_UPDATE 8 +#define CONTROL_BTN_SETTINGS 9 +#define CONTROL_BTN_CHANGELOG 10 +#define CONTROL_BTN_ROLLBACK 11 +#define CONTROL_BTN_SELECT 12 + +using namespace std; +using namespace ADDON; +using namespace XFILE; + +CGUIDialogAddonInfo::CGUIDialogAddonInfo(void) + : CGUIDialog(WINDOW_DIALOG_ADDON_INFO, "DialogAddonInfo.xml"), m_jobid(0) +{ + m_item = CFileItemPtr(new CFileItem); + m_loadType = KEEP_IN_MEMORY; +} + +CGUIDialogAddonInfo::~CGUIDialogAddonInfo(void) +{ +} + +bool CGUIDialogAddonInfo::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_DEINIT: + { + if (m_jobid) + CJobManager::GetInstance().CancelJob(m_jobid); + } + break; + + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_BTN_UPDATE) + { + OnUpdate(); + return true; + } + if (iControl == CONTROL_BTN_INSTALL) + { + if (!m_localAddon) + { + OnInstall(); + return true; + } + else + { + OnUninstall(); + return true; + } + } + else if (iControl == CONTROL_BTN_SELECT) + { + OnLaunch(); + return true; + } + else if (iControl == CONTROL_BTN_ENABLE) + { + OnEnable(!m_item->GetProperty("Addon.Enabled").asBoolean()); + return true; + } + else if (iControl == CONTROL_BTN_SETTINGS) + { + OnSettings(); + return true; + } + else if (iControl == CONTROL_BTN_CHANGELOG) + { + OnChangeLog(); + return true; + } + else if (iControl == CONTROL_BTN_ROLLBACK) + { + OnRollback(); + return true; + } + } + break; +default: + break; + } + + return CGUIDialog::OnMessage(message); +} + +bool CGUIDialogAddonInfo::OnAction(const CAction &action) +{ + if (action.GetID() == ACTION_SHOW_INFO) + { + Close(); + return true; + } + return CGUIDialog::OnAction(action); +} + +void CGUIDialogAddonInfo::OnInitWindow() +{ + UpdateControls(); + CGUIDialog::OnInitWindow(); + m_changelog = false; +} + +void CGUIDialogAddonInfo::UpdateControls() +{ + bool isInstalled = NULL != m_localAddon.get(); + bool isEnabled = isInstalled && m_item->GetProperty("Addon.Enabled").asBoolean(); + bool isUpdatable = isInstalled && m_item->GetProperty("Addon.UpdateAvail").asBoolean(); + bool isExecutable = isInstalled && (m_localAddon->Type() == ADDON_PLUGIN || m_localAddon->Type() == ADDON_SCRIPT); + if (isInstalled) + GrabRollbackVersions(); + + bool canDisable = isInstalled && CAddonMgr::Get().CanAddonBeDisabled(m_localAddon->ID()); + bool canInstall = !isInstalled && m_item->GetProperty("Addon.Broken").empty(); + bool isRepo = (isInstalled && m_localAddon->Type() == ADDON_REPOSITORY) || (m_addon && m_addon->Type() == ADDON_REPOSITORY); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canDisable || canInstall); + SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, isInstalled ? 24037 : 24038); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, canDisable); + SET_CONTROL_LABEL(CONTROL_BTN_ENABLE, isEnabled ? 24021 : 24022); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_UPDATE, isUpdatable); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_SETTINGS, isInstalled && m_localAddon->HasSettings()); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_SELECT, isExecutable); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_CHANGELOG, !isRepo); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ROLLBACK, m_rollbackVersions.size() > 1); +} + +void CGUIDialogAddonInfo::OnUpdate() +{ + std::string referer = StringUtils::Format("Referer=%s-%s.zip",m_localAddon->ID().c_str(),m_localAddon->Version().asString().c_str()); + CAddonInstaller::Get().Install(m_addon->ID(), true, referer); // force install + Close(); +} + +void CGUIDialogAddonInfo::OnInstall() +{ + CAddonInstaller::Get().Install(m_addon->ID()); + Close(); +} + +void CGUIDialogAddonInfo::OnLaunch() +{ + if (!m_localAddon) + return; + + CBuiltins::Execute("RunAddon(" + m_localAddon->ID() + ")"); + Close(); +} + +bool CGUIDialogAddonInfo::PromptIfDependency(int heading, int line2) +{ + if (!m_localAddon) + return false; + + VECADDONS addons; + vector deps; + CAddonMgr::Get().GetAllAddons(addons); + for (VECADDONS::const_iterator it = addons.begin(); + it != addons.end();++it) + { + ADDONDEPS::const_iterator i = (*it)->GetDeps().find(m_localAddon->ID()); + if (i != (*it)->GetDeps().end() && !i->second.second) // non-optional dependency + deps.push_back((*it)->Name()); + } + + if (!deps.empty()) + { + string line0 = StringUtils::Format(g_localizeStrings.Get(24046).c_str(), m_localAddon->Name().c_str()); + string line1 = StringUtils::Join(deps, ", "); + CGUIDialogOK::ShowAndGetInput(heading, line0, line1, line2); + return true; + } + return false; +} + +void CGUIDialogAddonInfo::OnUninstall() +{ + if (!m_localAddon.get()) + return; + + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return; + + // ensure the addon is not a dependency of other installed addons + if (PromptIfDependency(24037, 24047)) + return; + + // prompt user to be sure + if (!CGUIDialogYesNo::ShowAndGetInput(24037, 750, 0, 0)) + return; + + // ensure the addon isn't disabled in our database + CAddonMgr::Get().DisableAddon(m_localAddon->ID(), false); + + CJobManager::GetInstance().AddJob(new CAddonUnInstallJob(m_localAddon), + &CAddonInstaller::Get()); + CAddonMgr::Get().RemoveAddon(m_localAddon->ID()); + Close(); +} + +void CGUIDialogAddonInfo::OnEnable(bool enable) +{ + if (!m_localAddon.get()) + return; + + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return; + + if (!enable && PromptIfDependency(24075, 24091)) + return; + + CAddonMgr::Get().DisableAddon(m_localAddon->ID(), !enable); + SetItem(m_item); + UpdateControls(); + g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); +} + +void CGUIDialogAddonInfo::OnSettings() +{ + CGUIDialogAddonSettings::ShowAndGetInput(m_localAddon); +} + +void CGUIDialogAddonInfo::OnChangeLog() +{ + CGUIDialogTextViewer* pDlgInfo = (CGUIDialogTextViewer*)g_windowManager.GetWindow(WINDOW_DIALOG_TEXT_VIEWER); + std::string name; + if (m_addon) + name = m_addon->Name(); + else if (m_localAddon) + name = m_localAddon->Name(); + pDlgInfo->SetHeading(g_localizeStrings.Get(24054)+" - "+name); + if (m_item->GetProperty("Addon.Changelog").empty()) + { + pDlgInfo->SetText(g_localizeStrings.Get(13413)); + CFileItemList items; + if (m_localAddon && + !m_item->GetProperty("Addon.UpdateAvail").asBoolean()) + { + items.Add(CFileItemPtr(new CFileItem(m_localAddon->ChangeLog(),false))); + } + else + items.Add(CFileItemPtr(new CFileItem(m_addon->ChangeLog(),false))); + items[0]->Select(true); + m_jobid = CJobManager::GetInstance().AddJob( + new CFileOperationJob(CFileOperationJob::ActionCopy,items, + "special://temp/"),this); + } + else + pDlgInfo->SetText(m_item->GetProperty("Addon.Changelog").asString()); + + m_changelog = true; + pDlgInfo->DoModal(); + m_changelog = false; +} + +void CGUIDialogAddonInfo::OnRollback() +{ + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return; + + CGUIDialogContextMenu* dlg = (CGUIDialogContextMenu*)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU); + CAddonDatabase database; + database.Open(); + + CContextButtons buttons; + for (unsigned int i=0;iVersion().asString()) + label += " "+g_localizeStrings.Get(24094); + if (database.IsAddonBlacklisted(m_localAddon->ID(),label)) + label += " "+g_localizeStrings.Get(24095); + + buttons.Add(i,label); + } + int choice; + if ((choice=dlg->ShowAndGetChoice(buttons)) > -1) + { + // blacklist everything newer + for (unsigned int j=choice+1;jID(),m_rollbackVersions[j]); + std::string path = "special://home/addons/packages/"; + path += m_localAddon->ID()+"-"+m_rollbackVersions[choice]+".zip"; + // needed as cpluff won't downgrade + if (!m_localAddon->IsType(ADDON_SERVICE)) + //we will handle this for service addons in CAddonInstallJob::OnPostInstall + CAddonMgr::Get().RemoveAddon(m_localAddon->ID()); + CAddonInstaller::Get().InstallFromZip(path); + database.RemoveAddonFromBlacklist(m_localAddon->ID(),m_rollbackVersions[choice]); + Close(); + } +} + +bool CGUIDialogAddonInfo::ShowForItem(const CFileItemPtr& item) +{ + CGUIDialogAddonInfo* dialog = (CGUIDialogAddonInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_INFO); + if (!dialog) + return false; + if (!dialog->SetItem(item)) + return false; + + dialog->DoModal(); + return true; +} + +bool CGUIDialogAddonInfo::SetItem(const CFileItemPtr& item) +{ + *m_item = *item; + m_rollbackVersions.clear(); + + // grab the local addon, if it's available + m_localAddon.reset(); + m_addon.reset(); + if (CAddonMgr::Get().GetAddon(item->GetProperty("Addon.ID").asString(), m_localAddon)) // sets m_localAddon if installed regardless of enabled state + m_item->SetProperty("Addon.Enabled", "true"); + else + m_item->SetProperty("Addon.Enabled", "false"); + m_item->SetProperty("Addon.Installed", m_localAddon ? "true" : "false"); + + CAddonDatabase database; + database.Open(); + database.GetAddon(item->GetProperty("Addon.ID").asString(),m_addon); + + if (TranslateType(item->GetProperty("Addon.intType").asString()) == ADDON_REPOSITORY) + { + CAddonDatabase database; + database.Open(); + VECADDONS addons; + if (m_addon) + database.GetRepository(m_addon->ID(), addons); + else if (m_localAddon) // sanity + database.GetRepository(m_localAddon->ID(), addons); + int tot=0; + for (int i = ADDON_UNKNOWN+1;iType() == (TYPE)i) + ++num; + } + m_item->SetProperty("Repo." + TranslateType((TYPE)i), num); + tot += num; + } + m_item->SetProperty("Repo.Addons", tot); + } + return true; +} + +void CGUIDialogAddonInfo::OnJobComplete(unsigned int jobID, bool success, + CJob* job) +{ + if (!m_changelog) + return; + + CGUIDialogTextViewer* pDlgInfo = (CGUIDialogTextViewer*)g_windowManager.GetWindow(WINDOW_DIALOG_TEXT_VIEWER); + + m_jobid = 0; + if (!success) + { + pDlgInfo->SetText(g_localizeStrings.Get(195)); + } + else + { + CFile file; + XFILE::auto_buffer buf; + if (file.LoadFile("special://temp/" + + URIUtils::GetFileName(((CFileOperationJob*)job)->GetItems()[0]->GetPath()), buf) > 0) + { + std::string str(buf.get(), buf.length()); + m_item->SetProperty("Addon.Changelog", str); + pDlgInfo->SetText(str); + } + } + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, WINDOW_DIALOG_TEXT_VIEWER, 0, GUI_MSG_UPDATE); + g_windowManager.SendThreadMessage(msg); +} + +void CGUIDialogAddonInfo::GrabRollbackVersions() +{ + CFileItemList items; + XFILE::CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS); + items.Sort(SortByLabel, SortOrderAscending); + CAddonDatabase db; + db.Open(); + for (int i=0;im_bIsFolder) + continue; + std::string ID, version; + AddonVersion::SplitFileName(ID,version,items[i]->GetLabel()); + if (ID == m_localAddon->ID()) + { + std::string hash, path(items[i]->GetPath()); + if (db.GetPackageHash(m_localAddon->ID(), path, hash)) + { + std::string md5 = CUtil::GetFileMD5(path); + if (md5 == hash) + m_rollbackVersions.push_back(version); + else /* The package has been corrupted */ + { + CLog::Log(LOGWARNING, "%s: Removing corrupt addon package %s.", __FUNCTION__, path.c_str()); + CFile::Delete(path); + db.RemovePackage(path); + } + } + } + } +} diff --git a/xbmc/addons/GUIDialogAddonInfo.h b/xbmc/addons/GUIDialogAddonInfo.h new file mode 100644 index 0000000..53b7a27 --- /dev/null +++ b/xbmc/addons/GUIDialogAddonInfo.h @@ -0,0 +1,80 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "guilib/GUIDialog.h" +#include "addons/IAddon.h" +#include "utils/Job.h" + +class CGUIDialogAddonInfo : + public CGUIDialog, + public IJobCallback +{ +public: + CGUIDialogAddonInfo(void); + virtual ~CGUIDialogAddonInfo(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction &action); + + virtual CFileItemPtr GetCurrentListItem(int offset = 0) { return m_item; } + virtual bool HasListItems() const { return true; } + + static bool ShowForItem(const CFileItemPtr& item); + + // job callback + void OnJobComplete(unsigned int jobID, bool success, CJob* job); +protected: + void OnInitWindow(); + + /*! \brief Set the item to display addon info on. + \param item to display + \return true if we can display information, false otherwise + */ + bool SetItem(const CFileItemPtr &item); + void UpdateControls(); + + void OnUpdate(); + void OnInstall(); + void OnUninstall(); + void OnEnable(bool enable); + void OnSettings(); + void OnChangeLog(); + void OnRollback(); + void OnLaunch(); + + /*! \brief check if the add-on is a dependency of others, and if so prompt the user. + \param heading the label for the heading of the prompt dialog + \param line2 the action that could not be completed. + \return true if prompted, false otherwise. + */ + bool PromptIfDependency(int heading, int line2); + + CFileItemPtr m_item; + ADDON::AddonPtr m_addon; + ADDON::AddonPtr m_localAddon; + unsigned int m_jobid; + bool m_changelog; + + // rollback data + void GrabRollbackVersions(); + std::vector m_rollbackVersions; +}; + diff --git a/xbmc/addons/GUIDialogAddonSettings.cpp b/xbmc/addons/GUIDialogAddonSettings.cpp new file mode 100644 index 0000000..ef1c3c2 --- /dev/null +++ b/xbmc/addons/GUIDialogAddonSettings.cpp @@ -0,0 +1,1200 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "GUIDialogAddonSettings.h" +#include "filesystem/PluginDirectory.h" +#include "addons/IAddon.h" +#include "addons/AddonManager.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogOK.h" +#include "guilib/GUIControlGroupList.h" +#include "guilib/GUISettingsSliderControl.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "storage/MediaManager.h" +#include "guilib/GUILabelControl.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIImage.h" +#include "input/Key.h" +#include "filesystem/Directory.h" +#include "video/VideoInfoScanner.h" +#include "addons/Scraper.h" +#include "guilib/GUIWindowManager.h" +#include "ApplicationMessenger.h" +#include "guilib/GUIKeyboardFactory.h" +#include "FileItem.h" +#include "settings/AdvancedSettings.h" +#include "settings/MediaSourceSettings.h" +#include "GUIInfoManager.h" +#include "GUIUserMessages.h" +#include "dialogs/GUIDialogSelect.h" +#include "GUIWindowAddonBrowser.h" +#include "utils/log.h" +#include "Util.h" +#include "URL.h" +#include "utils/XMLUtils.h" + +using namespace std; +using namespace ADDON; +using XFILE::CDirectory; + +#define CONTROL_SETTINGS_AREA 2 +#define CONTROL_DEFAULT_BUTTON 3 +#define CONTROL_DEFAULT_RADIOBUTTON 4 +#define CONTROL_DEFAULT_SPIN 5 +#define CONTROL_DEFAULT_SEPARATOR 6 +#define CONTROL_DEFAULT_LABEL_SEPARATOR 7 +#define CONTROL_DEFAULT_SLIDER 8 +#define CONTROL_SECTION_AREA 9 +#define CONTROL_DEFAULT_SECTION_BUTTON 13 + +#define ID_BUTTON_OK 10 +#define ID_BUTTON_CANCEL 11 +#define ID_BUTTON_DEFAULT 12 +#define CONTROL_HEADING_LABEL 20 + +#define CONTROL_START_SECTION 100 +#define CONTROL_START_SETTING 200 + +CGUIDialogAddonSettings::CGUIDialogAddonSettings() + : CGUIDialogBoxBase(WINDOW_DIALOG_ADDON_SETTINGS, "DialogAddonSettings.xml") +{ + m_currentSection = 0; + m_totalSections = 1; + m_saveToDisk = false; +} + +CGUIDialogAddonSettings::~CGUIDialogAddonSettings(void) +{ +} + +bool CGUIDialogAddonSettings::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + FreeSections(); + } + break; + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + bool bCloseDialog = false; + + if (iControl == ID_BUTTON_DEFAULT) + SetDefaultSettings(); + else if (iControl != ID_BUTTON_OK) + bCloseDialog = ShowVirtualKeyboard(iControl); + + if (iControl == ID_BUTTON_OK || iControl == ID_BUTTON_CANCEL || bCloseDialog) + { + if (iControl == ID_BUTTON_OK || bCloseDialog) + { + m_bConfirmed = true; + SaveSettings(); + } + Close(); + return true; + } + } + break; + case GUI_MSG_FOCUSED: + { + CGUIDialogBoxBase::OnMessage(message); + int focusedControl = GetFocusedControlID(); + if (focusedControl >= CONTROL_START_SECTION && focusedControl < (int)(CONTROL_START_SECTION + m_totalSections) && + focusedControl - CONTROL_START_SECTION != (int)m_currentSection) + { // changing section + UpdateFromControls(); + m_currentSection = focusedControl - CONTROL_START_SECTION; + CreateControls(); + } + return true; + } + case GUI_MSG_SETTING_UPDATED: + { + std::string id = message.GetStringParam(0); + std::string value = message.GetStringParam(1); + m_settings[id] = value; + if (GetFocusedControl()) + { + int iControl = GetFocusedControl()->GetID(); + CreateControls(); + CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl); + OnMessage(msg); + } + return true; + } + } + return CGUIDialogBoxBase::OnMessage(message); +} + +bool CGUIDialogAddonSettings::OnAction(const CAction& action) +{ + if (action.GetID() == ACTION_DELETE_ITEM) + { + CGUIControl* pControl = GetFocusedControl(); + if (pControl) + { + int iControl = pControl->GetID(); + int controlId = CONTROL_START_SETTING; + const TiXmlElement* setting = GetFirstSetting(); + UpdateFromControls(); + while (setting) + { + if (controlId == iControl) + { + const char* id = setting->Attribute("id"); + const char* value = setting->Attribute("default"); + if (id && value) + m_settings[id] = value; + CreateControls(); + CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl); + OnMessage(msg); + return true; + } + setting = setting->NextSiblingElement("setting"); + controlId++; + } + } + } + return CGUIDialogBoxBase::OnAction(action); +} + +void CGUIDialogAddonSettings::OnInitWindow() +{ + m_currentSection = 0; + m_totalSections = 1; + CreateSections(); + CreateControls(); + CGUIDialogBoxBase::OnInitWindow(); +} + +// \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. +bool CGUIDialogAddonSettings::ShowAndGetInput(const AddonPtr &addon, bool saveToDisk /* = true */) +{ + if (!addon) + return false; + + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + + bool ret(false); + if (addon->HasSettings()) + { + // Create the dialog + CGUIDialogAddonSettings* pDialog = NULL; + pDialog = (CGUIDialogAddonSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_SETTINGS); + if (!pDialog) + return false; + + // Set the heading + std::string heading = StringUtils::Format("$LOCALIZE[10004] - %s", addon->Name().c_str()); // "Settings - AddonName" + pDialog->m_strHeading = heading; + + pDialog->m_addon = addon; + pDialog->m_saveToDisk = saveToDisk; + pDialog->DoModal(); + ret = true; + } + else + { // addon does not support settings, inform user + CGUIDialogOK::ShowAndGetInput(24000,0,24030,0); + } + + return ret; +} + +bool CGUIDialogAddonSettings::ShowVirtualKeyboard(int iControl) +{ + int controlId = CONTROL_START_SETTING; + bool bCloseDialog = false; + + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + if (controlId == iControl) + { + const CGUIControl* control = GetControl(controlId); + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + + //Special handling for actions: does not require id attribute. TODO: refactor me. + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON && type == "action") + { + const char *option = setting->Attribute("option"); + std::string action = XMLUtils::GetAttribute(setting, "action"); + if (!action.empty()) + { + // replace $CWD with the url of plugin/script + StringUtils::Replace(action, "$CWD", m_addon->Path()); + StringUtils::Replace(action, "$ID", m_addon->ID()); + if (option) + bCloseDialog = (strcmpi(option, "close") == 0); + CApplicationMessenger::Get().ExecBuiltIn(action); + } + break; + } + + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON && + !id.empty() && !type.empty()) + { + const char *option = setting->Attribute("option"); + const char *source = setting->Attribute("source"); + std::string value = m_buttonValues[id]; + std::string label = GetString(setting->Attribute("label")); + + if (type == "text") + { + // get any options + bool bHidden = false; + bool bEncoded = false; + if (option) + { + bHidden = (strstr(option, "hidden") != NULL); + bEncoded = (strstr(option, "urlencoded") != NULL); + } + if (bEncoded) + value = CURL::Decode(value); + + if (CGUIKeyboardFactory::ShowAndGetInput(value, label, true, bHidden)) + { + // if hidden hide input + if (bHidden) + { + std::string hiddenText; + hiddenText.append(value.size(), L'*'); + ((CGUIButtonControl *)control)->SetLabel2(hiddenText); + } + else + ((CGUIButtonControl*) control)->SetLabel2(value); + if (bEncoded) + value = CURL::Encode(value); + } + } + else if (type == "number" && CGUIDialogNumeric::ShowAndGetNumber(value, label)) + { + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else if (type == "ipaddress" && CGUIDialogNumeric::ShowAndGetIPAddress(value, label)) + { + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else if (type == "select") + { + CGUIDialogSelect *pDlg = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + if (pDlg) + { + pDlg->SetHeading(label.c_str()); + pDlg->Reset(); + + int selected = -1; + vector valuesVec; + if (setting->Attribute("values")) + StringUtils::Tokenize(setting->Attribute("values"), valuesVec, "|"); + else if (setting->Attribute("lvalues")) + { // localize + StringUtils::Tokenize(setting->Attribute("lvalues"), valuesVec, "|"); + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + if (i == (unsigned int)atoi(value.c_str())) + selected = i; + std::string localized = m_addon->GetString(atoi(valuesVec[i].c_str())); + if (localized.empty()) + localized = g_localizeStrings.Get(atoi(valuesVec[i].c_str())); + valuesVec[i] = localized; + } + } + else if (source) + { + valuesVec = GetFileEnumValues(source, XMLUtils::GetAttribute(setting, "mask"), XMLUtils::GetAttribute(setting, "option")); + } + + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + pDlg->Add(valuesVec[i]); + if (selected == (int)i || (selected < 0 && StringUtils::EqualsNoCase(valuesVec[i], value))) + pDlg->SetSelected(i); // FIXME: the SetSelected() does not select "i", it always defaults to the first position + } + pDlg->DoModal(); + int iSelected = pDlg->GetSelectedLabel(); + if (iSelected >= 0) + { + if (setting->Attribute("lvalues")) + value = StringUtils::Format("%i", iSelected); + else + value = valuesVec[iSelected]; + ((CGUIButtonControl*) control)->SetLabel2(valuesVec[iSelected]); + } + } + } + else if (type == "audio" || type == "video" + || type == "image" || type == "executable" + || type == "file" || type == "folder") + { + // setup the shares + VECSOURCES *shares = NULL; + if (source && strcmpi(source, "") != 0) + shares = CMediaSourceSettings::Get().GetSources(source); + + VECSOURCES localShares; + if (!shares) + { + g_mediaManager.GetLocalDrives(localShares); + if (!source || strcmpi(source, "local") != 0) + g_mediaManager.GetNetworkLocations(localShares); + } + else // always append local drives + { + localShares = *shares; + g_mediaManager.GetLocalDrives(localShares); + } + + if (type == "folder") + { + // get any options + bool bWriteOnly = false; + if (option) + bWriteOnly = (strcmpi(option, "writeable") == 0); + + if (CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, label, value, bWriteOnly)) + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else if (type == "image") + { + if (CGUIDialogFileBrowser::ShowAndGetImage(localShares, label, value)) + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else + { + // set the proper mask + std::string strMask; + if (setting->Attribute("mask")) + { + strMask = setting->Attribute("mask"); + // convert mask qualifiers + StringUtils::Replace(strMask, "$AUDIO", g_advancedSettings.m_musicExtensions); + StringUtils::Replace(strMask, "$VIDEO", g_advancedSettings.m_videoExtensions); + StringUtils::Replace(strMask, "$IMAGE", g_advancedSettings.m_pictureExtensions); +#if defined(_WIN32_WINNT) + StringUtils::Replace(strMask, "$EXECUTABLE", ".exe|.bat|.cmd|.py"); +#else + StringUtils::Replace(strMask, "$EXECUTABLE", ""); +#endif + } + else + { + if (type == "video") + strMask = g_advancedSettings.m_videoExtensions; + else if (type == "audio") + strMask = g_advancedSettings.m_musicExtensions; + else if (type == "executable") +#if defined(_WIN32_WINNT) + strMask = ".exe|.bat|.cmd|.py"; +#else + strMask = ""; +#endif + } + + // get any options + bool bUseThumbs = false; + bool bUseFileDirectories = false; + if (option) + { + vector options = StringUtils::Split(option, '|'); + bUseThumbs = find(options.begin(), options.end(), "usethumbs") != options.end(); + bUseFileDirectories = find(options.begin(), options.end(), "treatasfolder") != options.end(); + } + + if (CGUIDialogFileBrowser::ShowAndGetFile(localShares, strMask, label, value, bUseThumbs, bUseFileDirectories)) + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (type == "date") + { + CDateTime date; + if (!value.empty()) + date.SetFromDBDate(value); + SYSTEMTIME timedate; + date.GetAsSystemTime(timedate); + if(CGUIDialogNumeric::ShowAndGetDate(timedate, label)) + { + date = timedate; + value = date.GetAsDBDate(); + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (type == "time") + { + SYSTEMTIME timedate; + if (value.size() >= 5) + { + // assumes HH:MM + timedate.wHour = atoi(value.substr(0, 2).c_str()); + timedate.wMinute = atoi(value.substr(3, 2).c_str()); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, label)) + { + value = StringUtils::Format("%02d:%02d", timedate.wHour, timedate.wMinute); + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (type == "addon") + { + const char *strType = setting->Attribute("addontype"); + if (strType) + { + vector addonTypes = StringUtils::Split(strType, ','); + vector types; + for (vector::iterator i = addonTypes.begin(); i != addonTypes.end(); ++i) + { + StringUtils::Trim(*i); + ADDON::TYPE type = TranslateType(*i); + if (type != ADDON_UNKNOWN) + types.push_back(type); + } + if (types.size() > 0) + { + const char *strMultiselect = setting->Attribute("multiselect"); + bool multiSelect = strMultiselect && strcmpi(strMultiselect, "true") == 0; + if (multiSelect) + { + // construct vector of addon IDs (IDs are comma seperated in single string) + vector addonIDs = StringUtils::Split(value, ','); + if (CGUIWindowAddonBrowser::SelectAddonID(types, addonIDs, false) == 1) + { + value = StringUtils::Join(addonIDs, ","); + ((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value)); + } + } + else // no need of string splitting/joining if we select only 1 addon + if (CGUIWindowAddonBrowser::SelectAddonID(types, value, false) == 1) + ((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value)); + } + } + } + m_buttonValues[id] = value; + break; + } + } + setting = setting->NextSiblingElement("setting"); + controlId++; + } + EnableControls(); + return bCloseDialog; +} + +void CGUIDialogAddonSettings::UpdateFromControls() +{ + int controlID = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const CGUIControl* control = GetControl(controlID++); + + if (control) + { + std::string value; + switch (control->GetControlType()) + { + case CGUIControl::GUICONTROL_BUTTON: + value = m_buttonValues[id]; + break; + case CGUIControl::GUICONTROL_RADIO: + value = ((CGUIRadioButtonControl*) control)->IsSelected() ? "true" : "false"; + break; + case CGUIControl::GUICONTROL_SPINEX: + if (type == "fileenum" || type == "labelenum") + value = ((CGUISpinControlEx*) control)->GetLabel(); + else + value = StringUtils::Format("%i", ((CGUISpinControlEx*) control)->GetValue()); + break; + case CGUIControl::GUICONTROL_SETTINGS_SLIDER: + { + std::string option = XMLUtils::GetAttribute(setting, "option"); + if (option.size() == 0 || StringUtils::EqualsNoCase(option, "float")) + value = StringUtils::Format("%f", ((CGUISettingsSliderControl *)control)->GetFloatValue()); + else + value = StringUtils::Format("%i", ((CGUISettingsSliderControl *)control)->GetIntValue()); + } + break; + default: + break; + } + m_settings[id] = value; + } + + setting = setting->NextSiblingElement("setting"); + } +} + +void CGUIDialogAddonSettings::SaveSettings(void) +{ + UpdateFromControls(); + + for (map::iterator i = m_settings.begin(); i != m_settings.end(); ++i) + m_addon->UpdateSetting(i->first, i->second); + + if (m_saveToDisk) + { + m_addon->SaveSettings(); + } +} + +void CGUIDialogAddonSettings::FreeSections() +{ + CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SECTION_AREA)); + if (group) + { + group->FreeResources(); + group->ClearAll(); + } + m_settings.clear(); + m_buttonValues.clear(); + FreeControls(); +} + +void CGUIDialogAddonSettings::FreeControls() +{ + // clear the category group + CGUIControlGroupList *control = dynamic_cast(GetControl(CONTROL_SETTINGS_AREA)); + if (control) + { + control->FreeResources(); + control->ClearAll(); + } +} + +void CGUIDialogAddonSettings::CreateSections() +{ + CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SECTION_AREA)); + CGUIButtonControl *originalButton = dynamic_cast(GetControl(CONTROL_DEFAULT_SECTION_BUTTON)); + if (!m_addon) + return; + + if (originalButton) + originalButton->SetVisible(false); + + // clear the category group + FreeSections(); + + // grab our categories + const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = m_addon->GetSettingsXML(); + + int buttonID = CONTROL_START_SECTION; + while (category) + { // add a category + CGUIButtonControl *button = originalButton ? originalButton->Clone() : NULL; + + std::string label = GetString(category->Attribute("label")); + if (label.empty()) + label = g_localizeStrings.Get(128); + + if (buttonID >= CONTROL_START_SETTING) + { + CLog::Log(LOGERROR, "%s - cannot have more than %d categories - simplify your addon!", __FUNCTION__, CONTROL_START_SETTING - CONTROL_START_SECTION); + break; + } + + // add the category button + if (button && group) + { + button->SetID(buttonID++); + button->SetLabel(label); + button->SetVisible(true); + group->AddControl(button); + } + + // grab a local copy of all the settings in this category + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + if (!id.empty()) + m_settings[id] = m_addon->GetSetting(id); + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + m_totalSections = buttonID - CONTROL_START_SECTION; +} + +void CGUIDialogAddonSettings::CreateControls() +{ + FreeControls(); + + CGUISpinControlEx *pOriginalSpin = dynamic_cast(GetControl(CONTROL_DEFAULT_SPIN)); + CGUIRadioButtonControl *pOriginalRadioButton = dynamic_cast(GetControl(CONTROL_DEFAULT_RADIOBUTTON)); + CGUIButtonControl *pOriginalButton = dynamic_cast(GetControl(CONTROL_DEFAULT_BUTTON)); + CGUIImage *pOriginalImage = dynamic_cast(GetControl(CONTROL_DEFAULT_SEPARATOR)); + CGUILabelControl *pOriginalLabel = dynamic_cast(GetControl(CONTROL_DEFAULT_LABEL_SEPARATOR)); + CGUISettingsSliderControl *pOriginalSlider = dynamic_cast(GetControl(CONTROL_DEFAULT_SLIDER)); + + if (!m_addon || !pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage + || !pOriginalLabel || !pOriginalSlider) + return; + + pOriginalSpin->SetVisible(false); + pOriginalRadioButton->SetVisible(false); + pOriginalButton->SetVisible(false); + pOriginalImage->SetVisible(false); + pOriginalLabel->SetVisible(false); + pOriginalSlider->SetVisible(false); + + CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SETTINGS_AREA)); + if (!group) + return; + + // set our dialog heading + SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, m_strHeading); + + CGUIControl* pControl = NULL; + int controlId = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string values = XMLUtils::GetAttribute(setting, "values"); + const std::string lvalues = XMLUtils::GetAttribute(setting, "lvalues"); + const std::string entries = XMLUtils::GetAttribute(setting, "entries"); + const std::string defaultVal = XMLUtils::GetAttribute(setting, "default"); + const std::string subsetting = XMLUtils::GetAttribute(setting, "subsetting"); + const std::string label = GetString(setting->Attribute("label"), subsetting == "true"); + + bool bSort = XMLUtils::GetAttribute(setting, "sort") == "yes"; + if (!type.empty()) + { + bool isAddonSetting = false; + if (type == "text" || type == "ipaddress" + || type == "number" || type == "video" + || type == "audio" || type == "image" + || type == "folder" || type == "executable" + || type == "file" || type == "action" + || type == "date" || type == "time" + || type == "select" || (isAddonSetting = type == "addon")) + { + pControl = new CGUIButtonControl(*pOriginalButton); + if (!pControl) return; + ((CGUIButtonControl *)pControl)->SetLabel(label); + if (!id.empty()) + { + std::string value = m_settings[id]; + m_buttonValues[id] = value; + // get any option to test for hidden + const std::string option = XMLUtils::GetAttribute(setting, "option"); + if (option == "urlencoded") + value = CURL::Decode(value); + else if (option == "hidden") + { + std::string hiddenText; + hiddenText.append(value.size(), L'*'); + ((CGUIButtonControl *)pControl)->SetLabel2(hiddenText); + } + else + { + if (isAddonSetting) + ((CGUIButtonControl *)pControl)->SetLabel2(GetAddonNames(value)); + else if (type == "select" && !lvalues.empty()) + { + vector valuesVec = StringUtils::Split(lvalues, '|'); + int selected = atoi(value.c_str()); + if (selected >= 0 && selected < (int)valuesVec.size()) + { + std::string label = m_addon->GetString(atoi(valuesVec[selected].c_str())); + if (label.empty()) + label = g_localizeStrings.Get(atoi(valuesVec[selected].c_str())); + ((CGUIButtonControl *)pControl)->SetLabel2(label); + } + } + else + ((CGUIButtonControl *)pControl)->SetLabel2(value); + } + } + else + ((CGUIButtonControl *)pControl)->SetLabel2(defaultVal); + } + else if (type == "bool" && !id.empty()) + { + pControl = new CGUIRadioButtonControl(*pOriginalRadioButton); + if (!pControl) return; + ((CGUIRadioButtonControl *)pControl)->SetLabel(label); + ((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings[id] == "true"); + } + else if ((type == "enum" || type == "labelenum") && !id.empty()) + { + vector valuesVec; + vector entryVec; + + pControl = new CGUISpinControlEx(*pOriginalSpin); + if (!pControl) return; + ((CGUISpinControlEx *)pControl)->SetText(label); + + if (!lvalues.empty()) + StringUtils::Tokenize(lvalues, valuesVec, "|"); + else if (values == "$HOURS") + { + for (unsigned int i = 0; i < 24; i++) + { + CDateTime time(2000, 1, 1, i, 0, 0); + valuesVec.push_back(g_infoManager.LocalizeTime(time, TIME_FORMAT_HH_MM_XX)); + } + } + else + StringUtils::Tokenize(values, valuesVec, "|"); + if (!entries.empty()) + StringUtils::Tokenize(entries, entryVec, "|"); + + if(bSort && type == "labelenum") + std::sort(valuesVec.begin(), valuesVec.end(), sortstringbyname()); + + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + int iAdd = i; + if (entryVec.size() > i) + iAdd = atoi(entryVec[i].c_str()); + if (!lvalues.empty()) + { + std::string replace = m_addon->GetString(atoi(valuesVec[i].c_str())); + if (replace.empty()) + replace = g_localizeStrings.Get(atoi(valuesVec[i].c_str())); + ((CGUISpinControlEx *)pControl)->AddLabel(replace, iAdd); + } + else + ((CGUISpinControlEx *)pControl)->AddLabel(valuesVec[i], iAdd); + } + if (type == "labelenum") + { // need to run through all our settings and find the one that matches + ((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings[id]); + } + else + ((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings[id].c_str())); + + } + else if (type == "fileenum" && !id.empty()) + { + pControl = new CGUISpinControlEx(*pOriginalSpin); + if (!pControl) return; + ((CGUISpinControlEx *)pControl)->SetText(label); + ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f); + + vector items = GetFileEnumValues(values, XMLUtils::GetAttribute(setting, "mask"), XMLUtils::GetAttribute(setting, "option")); + for (unsigned int i = 0; i < items.size(); ++i) + { + ((CGUISpinControlEx *)pControl)->AddLabel(items[i], i); + if (StringUtils::EqualsNoCase(items[i], m_settings[id])) + ((CGUISpinControlEx *)pControl)->SetValue(i); + } + } + // Sample: + // in strings.xml: %2.0f mp + // creates 11 piece, text formated number labels from 0 to 100 + else if (type == "rangeofnum" && !id.empty()) + { + pControl = new CGUISpinControlEx(*pOriginalSpin); + if (!pControl) + return; + ((CGUISpinControlEx *)pControl)->SetText(label); + ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f); + + double rangestart = 0, rangeend = 1; + setting->Attribute("rangestart", &rangestart); + setting->Attribute("rangeend", &rangeend); + + int elements = 2; + setting->Attribute("elements", &elements); + + std::string valueformat; + if (setting->Attribute("valueformat")) + valueformat = m_addon->GetString(atoi(setting->Attribute("valueformat"))); + for (int i = 0; i < elements; i++) + { + std::string valuestring; + if (elements < 2) + valuestring = StringUtils::Format(valueformat.c_str(), rangestart); + else + valuestring = StringUtils::Format(valueformat.c_str(), rangestart+(rangeend-rangestart)/(elements-1)*i); + ((CGUISpinControlEx *)pControl)->AddLabel(valuestring, i); + } + ((CGUISpinControlEx *)pControl)->SetValue(atoi(m_settings[id].c_str())); + } + // Sample: + // to make ints from 5-60 with 5 steps + else if (type == "slider" && !id.empty()) + { + pControl = new CGUISettingsSliderControl(*pOriginalSlider); + if (!pControl) return; + ((CGUISettingsSliderControl *)pControl)->SetText(label); + + float fMin = 0.0f; + float fMax = 100.0f; + float fInc = 1.0f; + vector range = StringUtils::Split(XMLUtils::GetAttribute(setting, "range"), ','); + if (range.size() > 1) + { + fMin = (float)atof(range[0].c_str()); + if (range.size() > 2) + { + fMax = (float)atof(range[2].c_str()); + fInc = (float)atof(range[1].c_str()); + } + else + fMax = (float)atof(range[1].c_str()); + } + + std::string option = XMLUtils::GetAttribute(setting, "option"); + int iType=0; + + if (option.empty() || StringUtils::EqualsNoCase(option, "float")) + iType = SLIDER_CONTROL_TYPE_FLOAT; + else if (StringUtils::EqualsNoCase(option, "int")) + iType = SLIDER_CONTROL_TYPE_INT; + else if (StringUtils::EqualsNoCase(option, "percent")) + iType = SLIDER_CONTROL_TYPE_PERCENTAGE; + + ((CGUISettingsSliderControl *)pControl)->SetType(iType); + ((CGUISettingsSliderControl *)pControl)->SetFloatRange(fMin, fMax); + ((CGUISettingsSliderControl *)pControl)->SetFloatInterval(fInc); + ((CGUISettingsSliderControl *)pControl)->SetFloatValue((float)atof(m_settings[id].c_str())); + } + else if (type == "lsep") + { + pControl = new CGUILabelControl(*pOriginalLabel); + if (pControl) + ((CGUILabelControl *)pControl)->SetLabel(label); + } + else if (type == "sep") + pControl = new CGUIImage(*pOriginalImage); + } + + if (pControl) + { + pControl->SetWidth(group->GetWidth()); + pControl->SetVisible(true); + pControl->SetID(controlId); + pControl->AllocResources(); + group->AddControl(pControl); + pControl = NULL; + } + + setting = setting->NextSiblingElement("setting"); + controlId++; + } + EnableControls(); +} + +std::string CGUIDialogAddonSettings::GetAddonNames(const std::string& addonIDslist) const +{ + std::string retVal; + vector addons = StringUtils::Split(addonIDslist, ','); + for (vector::const_iterator it = addons.begin(); it != addons.end() ; ++it) + { + if (!retVal.empty()) + retVal += ", "; + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(*it ,addon)) + retVal += addon->Name(); + else + retVal += *it; + } + return retVal; +} + +vector CGUIDialogAddonSettings::GetFileEnumValues(const std::string &path, const std::string &mask, const std::string &options) const +{ + // Create our base path, used for type "fileenum" settings + // replace $PROFILE with the profile path of the plugin/script + std::string fullPath = path; + if (fullPath.find("$PROFILE") != std::string::npos) + StringUtils::Replace(fullPath, "$PROFILE", m_addon->Profile()); + else + fullPath = URIUtils::AddFileToFolder(m_addon->Path(), path); + + bool hideExtensions = StringUtils::EqualsNoCase(options, "hideext"); + // fetch directory + CFileItemList items; + if (!mask.empty()) + CDirectory::GetDirectory(fullPath, items, mask, XFILE::DIR_FLAG_NO_FILE_DIRS); + else + CDirectory::GetDirectory(fullPath, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS); + + vector values; + for (int i = 0; i < items.Size(); ++i) + { + CFileItemPtr pItem = items[i]; + if ((mask == "/" && pItem->m_bIsFolder) || !pItem->m_bIsFolder) + { + if (hideExtensions) + pItem->RemoveExtension(); + values.push_back(pItem->GetLabel()); + } + } + return values; +} + +// Go over all the settings and set their enabled condition according to the values of the enabled attribute +void CGUIDialogAddonSettings::EnableControls() +{ + int controlId = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + const CGUIControl* control = GetControl(controlId); + if (control) + { + // set enable status + const char *enable = setting->Attribute("enable"); + if (enable) + ((CGUIControl*) control)->SetEnabled(GetCondition(enable, controlId)); + else + ((CGUIControl*) control)->SetEnabled(true); + // set visible status + const char *visible = setting->Attribute("visible"); + if (visible) + ((CGUIControl*) control)->SetVisible(GetCondition(visible, controlId)); + else + ((CGUIControl*) control)->SetVisible(true); + } + setting = setting->NextSiblingElement("setting"); + controlId++; + } +} + +bool CGUIDialogAddonSettings::GetCondition(const std::string &condition, const int controlId) +{ + if (condition.empty()) return true; + + bool bCondition = true; + bool bCompare = true; + bool bControlDependend = false;//flag if the condition depends on another control + vector conditionVec; + + if (condition.find("+") != std::string::npos) + StringUtils::Tokenize(condition, conditionVec, "+"); + else + { + bCondition = false; + bCompare = false; + StringUtils::Tokenize(condition, conditionVec, "|"); + } + + for (unsigned int i = 0; i < conditionVec.size(); i++) + { + vector condVec; + if (!TranslateSingleString(conditionVec[i], condVec)) continue; + + const CGUIControl* control2 = GetControl(controlId + atoi(condVec[1].c_str())); + if (!control2) + continue; + + bControlDependend = true; //once we are here - this condition depends on another control + + std::string value; + switch (control2->GetControlType()) + { + case CGUIControl::GUICONTROL_BUTTON: + value = ((CGUIButtonControl*) control2)->GetLabel2(); + break; + case CGUIControl::GUICONTROL_RADIO: + value = ((CGUIRadioButtonControl*) control2)->IsSelected() ? "true" : "false"; + break; + case CGUIControl::GUICONTROL_SPINEX: + if (((CGUISpinControlEx*) control2)->GetFloatValue() > 0.0f) + value = ((CGUISpinControlEx*) control2)->GetLabel(); + else + value = StringUtils::Format("%i", ((CGUISpinControlEx*) control2)->GetValue()); + break; + default: + break; + } + + if (condVec[0] == "eq") + { + if (bCompare) + bCondition &= StringUtils::EqualsNoCase(value, condVec[2]); + else + bCondition |= StringUtils::EqualsNoCase(value, condVec[2]); + } + else if (condVec[0] == "!eq") + { + if (bCompare) + bCondition &= !StringUtils::EqualsNoCase(value, condVec[2]); + else + bCondition |= !StringUtils::EqualsNoCase(value, condVec[2]); + } + else if (condVec[0] == "gt") + { + if (bCompare) + bCondition &= (atoi(value.c_str()) > atoi(condVec[2].c_str())); + else + bCondition |= (atoi(value.c_str()) > atoi(condVec[2].c_str())); + } + else if (condVec[0] == "lt") + { + if (bCompare) + bCondition &= (atoi(value.c_str()) < atoi(condVec[2].c_str())); + else + bCondition |= (atoi(value.c_str()) < atoi(condVec[2].c_str())); + } + } + + if (!bControlDependend)//if condition doesn't depend on another control - try if its an infobool expression + { + bCondition = g_infoManager.EvaluateBool(condition); + } + + return bCondition; +} + +bool CGUIDialogAddonSettings::TranslateSingleString(const std::string &strCondition, vector &condVec) +{ + std::string strTest = strCondition; + StringUtils::ToLower(strTest); + StringUtils::Trim(strTest); + + size_t pos1 = strTest.find("("); + size_t pos2 = strTest.find(",", pos1); + size_t pos3 = strTest.find(")", pos2); + if (pos1 != std::string::npos && + pos2 != std::string::npos && + pos3 != std::string::npos) + { + condVec.push_back(strTest.substr(0, pos1)); + condVec.push_back(strTest.substr(pos1 + 1, pos2 - pos1 - 1)); + condVec.push_back(strTest.substr(pos2 + 1, pos3 - pos2 - 1)); + return true; + } + return false; +} + +std::string CGUIDialogAddonSettings::GetString(const char *value, bool subSetting) const +{ + if (!value) + return ""; + std::string prefix(subSetting ? "- " : ""); + if (StringUtils::IsNaturalNumber(value)) + return prefix + m_addon->GetString(atoi(value)); + return prefix + value; +} + +// Go over all the settings and set their default values +void CGUIDialogAddonSettings::SetDefaultSettings() +{ + if(!m_addon) + return; + + const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = m_addon->GetSettingsXML(); + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const char *value = setting->Attribute("default"); + if (!id.empty()) + { + if (value) + m_settings[id] = value; + else if (type == "bool") + m_settings[id] = "false"; + else if (type == "slider" || type == "enum") + m_settings[id] = "0"; + else + m_settings[id] = ""; + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + CreateControls(); +} + +const TiXmlElement *CGUIDialogAddonSettings::GetFirstSetting() const +{ + const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) + category = m_addon->GetSettingsXML(); + for (unsigned int i = 0; i < m_currentSection && category; i++) + category = category->NextSiblingElement("category"); + if (category) + return category->FirstChildElement("setting"); + return NULL; +} + +void CGUIDialogAddonSettings::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + // update status of current section button + bool alphaFaded = false; + CGUIControl *control = GetFirstFocusableControl(CONTROL_START_SECTION + m_currentSection); + if (control && !control->HasFocus()) + { + if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + { + control->SetFocus(true); + ((CGUIButtonControl *)control)->SetAlpha(0x80); + alphaFaded = true; + } + else if (control->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON) + { + control->SetFocus(true); + ((CGUIButtonControl *)control)->SetSelected(true); + alphaFaded = true; + } + } + CGUIDialogBoxBase::DoProcess(currentTime, dirtyregions); + if (alphaFaded && m_active) // dialog may close + { + control->SetFocus(false); + if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + ((CGUIButtonControl *)control)->SetAlpha(0xFF); + else + ((CGUIButtonControl *)control)->SetSelected(false); + } +} + +std::string CGUIDialogAddonSettings::GetCurrentID() const +{ + if (m_addon) + return m_addon->ID(); + return ""; +} + +int CGUIDialogAddonSettings::GetDefaultLabelID(int controlId) const +{ + if (controlId == ID_BUTTON_OK) + return 186; + else if (controlId == ID_BUTTON_CANCEL) + return 222; + else if (controlId == ID_BUTTON_DEFAULT) + return 409; + + return CGUIDialogBoxBase::GetDefaultLabelID(controlId); +} diff --git a/xbmc/addons/GUIDialogAddonSettings.h b/xbmc/addons/GUIDialogAddonSettings.h new file mode 100644 index 0000000..9c9c156 --- /dev/null +++ b/xbmc/addons/GUIDialogAddonSettings.h @@ -0,0 +1,91 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "dialogs/GUIDialogBoxBase.h" +#include "addons/Addon.h" + +class CGUIDialogAddonSettings : public CGUIDialogBoxBase +{ +public: + CGUIDialogAddonSettings(void); + virtual ~CGUIDialogAddonSettings(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction& action); + /*! \brief Show the addon settings dialog, allowing the user to configure an addon + \param addon the addon to configure + \param saveToDisk whether the changes should be saved to disk or just made local to the addon. Defaults to true + \return true if settings were changed and the dialog confirmed, false otherwise. + */ + static bool ShowAndGetInput(const ADDON::AddonPtr &addon, bool saveToDisk = true); + virtual void DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions); + + std::string GetCurrentID() const; +protected: + virtual void OnInitWindow(); + virtual int GetDefaultLabelID(int controlId) const; + +private: + /*! \brief return a (localized) addon string. + \param value either a character string (which is used directly) or a number to lookup in the addons strings.xml + \param subsetting whether the character string should be prefixed by "- ", defaults to false + \return the localized addon string + */ + std::string GetString(const char *value, bool subSetting = false) const; + + /*! \brief return a the values for a fileenum setting + \param path the path to use for files + \param mask the mask to use + \param options any options, such as "hideext" to hide extensions + \return the filenames in the path that match the mask + */ + std::vector GetFileEnumValues(const std::string &path, const std::string &mask, const std::string &options) const; + + /*! \brief Translate list of addon IDs to list of addon names + \param addonIDslist comma seperated list of addon IDs + \return comma seperated list of addon names + */ + std::string GetAddonNames(const std::string& addonIDslist) const; + + void CreateSections(); + void FreeSections(); + void CreateControls(); + void FreeControls(); + void UpdateFromControls(); + void EnableControls(); + void SetDefaultSettings(); + bool GetCondition(const std::string &condition, const int controlId); + + void SaveSettings(void); + bool ShowVirtualKeyboard(int iControl); + bool TranslateSingleString(const std::string &strCondition, std::vector &enableVec); + + const TiXmlElement *GetFirstSetting() const; + + ADDON::AddonPtr m_addon; + std::map m_buttonValues; + bool m_saveToDisk; // whether the addon settings should be saved to disk or just stored locally in the addon + + unsigned int m_currentSection; + unsigned int m_totalSections; + + std::map m_settings; // local storage of values +}; + diff --git a/xbmc/addons/GUIViewStateAddonBrowser.cpp b/xbmc/addons/GUIViewStateAddonBrowser.cpp new file mode 100644 index 0000000..4f42b2e --- /dev/null +++ b/xbmc/addons/GUIViewStateAddonBrowser.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "GUIViewStateAddonBrowser.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "guilib/GraphicContext.h" +#include "guilib/WindowIDs.h" +#include "view/ViewState.h" +#include "addons/Addon.h" +#include "addons/AddonManager.h" +#include "addons/AddonInstaller.h" +#include "AddonDatabase.h" +#include "utils/StringUtils.h" + +using namespace XFILE; +using namespace ADDON; + +CGUIViewStateAddonBrowser::CGUIViewStateAddonBrowser(const CFileItemList& items) : CGUIViewState(items) +{ + if (items.IsVirtualDirectoryRoot()) + { + AddSortMethod(SortByNone, 551, LABEL_MASKS("%F", "", "%L", "")); + SetSortMethod(SortByNone); + } + else + { + AddSortMethod(SortByLabel, SortAttributeIgnoreFolders, 551, LABEL_MASKS("%L", "%I", "%L", "")); // Filename, Size | Foldername, empty + AddSortMethod(SortByDate, 552, LABEL_MASKS("%L", "%J", "%L", "%J")); // Filename, Date | Foldername, Date + SetSortMethod(SortByLabel); + } + SetViewAsControl(DEFAULT_VIEW_AUTO); + + SetSortOrder(SortOrderAscending); + LoadViewState(items.GetPath(), WINDOW_ADDON_BROWSER); +} + +void CGUIViewStateAddonBrowser::SaveViewState() +{ + SaveViewToDb(m_items.GetPath(), WINDOW_ADDON_BROWSER); +} + +std::string CGUIViewStateAddonBrowser::GetExtensions() +{ + return ""; +} + +VECSOURCES& CGUIViewStateAddonBrowser::GetSources() +{ + m_sources.clear(); + + { // check for updates + CMediaSource share; + share.strPath = "addons://check/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_REMOTE; // hack for sorting + share.strName = g_localizeStrings.Get(24055); // "Check for updates" + CDateTime lastChecked = CAddonInstaller::Get().LastRepoUpdate(); + if (lastChecked.IsValid()) + share.strStatus = StringUtils::Format(g_localizeStrings.Get(24056).c_str(), + lastChecked.GetAsLocalizedDateTime(false, false).c_str()); + m_sources.push_back(share); + } + if (CAddonMgr::Get().HasOutdatedAddons()) + { + CMediaSource share; + share.strPath = "addons://outdated/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24043); // "Available updates" + m_sources.push_back(share); + } + CAddonDatabase db; + if (db.Open() && db.HasDisabledAddons()) + { + CMediaSource share; + share.strPath = "addons://disabled/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24039); + m_sources.push_back(share); + } + // we always have some enabled addons + { + CMediaSource share; + share.strPath = "addons://enabled/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24062); + m_sources.push_back(share); + } + if (CAddonMgr::Get().HasAddons(ADDON_REPOSITORY,true)) + { + CMediaSource share; + share.strPath = "addons://repos/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24033); + m_sources.push_back(share); + } + // add "install from zip" + { + CMediaSource share; + share.strPath = "addons://install/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24041); + m_sources.push_back(share); + } + // add "search" + { + CMediaSource share; + share.strPath = "addons://search/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(137); + m_sources.push_back(share); + } + + return CGUIViewState::GetSources(); +} + diff --git a/xbmc/addons/GUIViewStateAddonBrowser.h b/xbmc/addons/GUIViewStateAddonBrowser.h new file mode 100644 index 0000000..565cdc8 --- /dev/null +++ b/xbmc/addons/GUIViewStateAddonBrowser.h @@ -0,0 +1,35 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "view/GUIViewState.h" + +class CGUIViewStateAddonBrowser : public CGUIViewState +{ +public: + CGUIViewStateAddonBrowser(const CFileItemList& items); + +protected: + virtual void SaveViewState(); + virtual std::string GetExtensions(); + virtual VECSOURCES& GetSources(); +}; + diff --git a/xbmc/addons/GUIWindowAddonBrowser.cpp b/xbmc/addons/GUIWindowAddonBrowser.cpp new file mode 100644 index 0000000..f4560dd --- /dev/null +++ b/xbmc/addons/GUIWindowAddonBrowser.cpp @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "GUIWindowAddonBrowser.h" +#include "addons/AddonManager.h" +#include "addons/Repository.h" +#include "GUIDialogAddonInfo.h" +#include "GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "GUIUserMessages.h" +#include "guilib/GUIWindowManager.h" +#include "utils/URIUtils.h" +#include "URL.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "filesystem/Directory.h" +#include "filesystem/AddonsDirectory.h" +#include "addons/AddonInstaller.h" +#include "utils/JobManager.h" +#include "utils/log.h" +#include "threads/SingleLock.h" +#include "settings/Settings.h" +#include "settings/MediaSourceSettings.h" +#include "utils/StringUtils.h" +#include "AddonDatabase.h" +#include "settings/AdvancedSettings.h" +#include "storage/MediaManager.h" +#include "LangInfo.h" +#include "input/Key.h" +#include "ContextMenuManager.h" + +#define CONTROL_AUTOUPDATE 5 +#define CONTROL_SHUTUP 6 +#define CONTROL_FOREIGNFILTER 7 +#define CONTROL_BROKENFILTER 8 + +using namespace ADDON; +using namespace XFILE; +using namespace std; + +CGUIWindowAddonBrowser::CGUIWindowAddonBrowser(void) +: CGUIMediaWindow(WINDOW_ADDON_BROWSER, "AddonBrowser.xml") +{ +} + +CGUIWindowAddonBrowser::~CGUIWindowAddonBrowser() +{ +} + +bool CGUIWindowAddonBrowser::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_DEINIT: + { + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + } + break; + case GUI_MSG_WINDOW_INIT: + { + m_rootDir.AllowNonLocalSources(false); + + // is this the first time the window is opened? + if (m_vecItems->GetPath() == "?" && message.GetStringParam().empty()) + m_vecItems->SetPath(""); + } + break; + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_AUTOUPDATE) + { + const CGUIControl *control = GetControl(CONTROL_AUTOUPDATE); + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + CSettings::Get().SetInt("general.addonupdates", (CSettings::Get().GetInt("general.addonupdates")+1) % AUTO_UPDATES_MAX); + else + CSettings::Get().SetInt("general.addonupdates", (CSettings::Get().GetInt("general.addonupdates") == 0) ? 1 : 0); + UpdateButtons(); + return true; + } + else if (iControl == CONTROL_SHUTUP) + { + CSettings::Get().ToggleBool("general.addonnotifications"); + CSettings::Get().Save(); + return true; + } + else if (iControl == CONTROL_FOREIGNFILTER) + { + CSettings::Get().ToggleBool("general.addonforeignfilter"); + CSettings::Get().Save(); + Refresh(); + return true; + } + else if (iControl == CONTROL_BROKENFILTER) + { + CSettings::Get().ToggleBool("general.addonbrokenfilter"); + CSettings::Get().Save(); + Refresh(); + return true; + } + else if (m_viewControl.HasControl(iControl)) // list/thumb control + { + // get selected item + int iItem = m_viewControl.GetSelectedItem(); + int iAction = message.GetParam1(); + + // iItem is checked for validity inside these routines + if (iAction == ACTION_SHOW_INFO) + { + if (!m_vecItems->Get(iItem)->GetProperty("Addon.ID").empty()) + return CGUIDialogAddonInfo::ShowForItem((*m_vecItems)[iItem]); + return false; + } + } + } + break; + case GUI_MSG_NOTIFY_ALL: + { + if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && IsActive() && message.GetNumStringParams() == 1) + { // update this item + for (int i = 0; i < m_vecItems->Size(); ++i) + { + CFileItemPtr item = m_vecItems->Get(i); + if (item->GetProperty("Addon.ID") == message.GetStringParam()) + { + SetItemLabel2(item); + return true; + } + } + } + } + break; + default: + break; + } + return CGUIMediaWindow::OnMessage(message); +} + +void CGUIWindowAddonBrowser::GetContextButtons(int itemNumber, CContextButtons& buttons) +{ + if (itemNumber < 0 || itemNumber >= m_vecItems->Size()) + return; + + CFileItemPtr pItem = m_vecItems->Get(itemNumber); + if (!pItem->IsPath("addons://enabled/")) + buttons.Add(CONTEXT_BUTTON_SCAN,24034); + + AddonPtr addon; + if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons + return; + + if (addon->Type() == ADDON_REPOSITORY && pItem->m_bIsFolder) + { + buttons.Add(CONTEXT_BUTTON_SCAN,24034); + buttons.Add(CONTEXT_BUTTON_REFRESH,24035); + } + + buttons.Add(CONTEXT_BUTTON_INFO,24003); + + if (addon->HasSettings()) + buttons.Add(CONTEXT_BUTTON_SETTINGS,24020); + + CContextMenuManager::Get().AddVisibleItems(pItem, buttons); +} + +bool CGUIWindowAddonBrowser::OnContextButton(int itemNumber, + CONTEXT_BUTTON button) +{ + CFileItemPtr pItem = m_vecItems->Get(itemNumber); + if (pItem->IsPath("addons://enabled/")) + { + if (button == CONTEXT_BUTTON_SCAN) + { + CAddonMgr::Get().FindAddons(); + return true; + } + } + + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) + { + if (button == CONTEXT_BUTTON_SETTINGS) + return CGUIDialogAddonSettings::ShowAndGetInput(addon); + + if (button == CONTEXT_BUTTON_REFRESH) + { + CAddonDatabase database; + database.Open(); + database.DeleteRepository(addon->ID()); + button = CONTEXT_BUTTON_SCAN; + } + + if (button == CONTEXT_BUTTON_SCAN) + { + CAddonInstaller::Get().UpdateRepos(true); + return true; + } + + if (button == CONTEXT_BUTTON_INFO) + { + CGUIDialogAddonInfo::ShowForItem(pItem); + return true; + } + } + + return CGUIMediaWindow::OnContextButton(itemNumber, button); +} + +class UpdateAddons : public IRunnable +{ + virtual void Run() + { + VECADDONS addons; + CAddonMgr::Get().GetAllOutdatedAddons(addons, true); // get local + for (VECADDONS::iterator i = addons.begin(); i != addons.end(); ++i) + { + std::string referer = StringUtils::Format("Referer=%s-%s.zip",(*i)->ID().c_str(),(*i)->Version().asString().c_str()); + CAddonInstaller::Get().Install((*i)->ID(), true, referer); // force install + } + } +}; + +class UpdateRepos : public IRunnable +{ + virtual void Run() + { + CAddonInstaller::Get().UpdateRepos(true, true); + } +}; + +bool CGUIWindowAddonBrowser::OnClick(int iItem) +{ + CFileItemPtr item = m_vecItems->Get(iItem); + if (item->GetPath() == "addons://install/") + { + // pop up filebrowser to grab an installed folder + VECSOURCES shares = *CMediaSourceSettings::Get().GetSources("files"); + g_mediaManager.GetLocalDrives(shares); + g_mediaManager.GetNetworkLocations(shares); + std::string path; + if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "*.zip", g_localizeStrings.Get(24041), path)) + CAddonInstaller::Get().InstallFromZip(path); + return true; + } + else if (item->GetPath() == "addons://check/") + { + // perform the check for updates + UpdateRepos updater; + if (CGUIDialogBusy::Wait(&updater)) + Refresh(); + return true; + } + if (item->GetPath() == "addons://update_all/") + { + // fire off a threaded update of all addons + UpdateAddons updater; + if (CGUIDialogBusy::Wait(&updater)) + return Update("addons://downloading/"); + return true; + } + if (!item->m_bIsFolder) + { + // cancel a downloading job + if (item->HasProperty("Addon.Downloading")) + { + if (CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24000), + item->GetProperty("Addon.Name").asString(), + g_localizeStrings.Get(24066),"")) + { + if (CAddonInstaller::Get().Cancel(item->GetProperty("Addon.ID").asString())) + Refresh(); + } + return true; + } + + CGUIDialogAddonInfo::ShowForItem(item); + return true; + } + if (item->IsPath("addons://search/")) + return Update(item->GetPath()); + + return CGUIMediaWindow::OnClick(iItem); +} + +void CGUIWindowAddonBrowser::UpdateButtons() +{ + const CGUIControl *control = GetControl(CONTROL_AUTOUPDATE); + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + { // set label + CSettingInt *setting = (CSettingInt *)CSettings::Get().GetSetting("general.addonupdates"); + if (setting) + { + const StaticIntegerSettingOptions& options = setting->GetOptions(); + for (StaticIntegerSettingOptions::const_iterator it = options.begin(); it != options.end(); ++it) + { + if (it->second == setting->GetValue()) + { + SET_CONTROL_LABEL(CONTROL_AUTOUPDATE, it->first); + break; + } + } + } + } + else + { // old skin with toggle button - set on if auto updates are on + SET_CONTROL_SELECTED(GetID(),CONTROL_AUTOUPDATE, CSettings::Get().GetInt("general.addonupdates") == AUTO_UPDATES_ON); + } + SET_CONTROL_SELECTED(GetID(),CONTROL_SHUTUP, CSettings::Get().GetBool("general.addonnotifications")); + SET_CONTROL_SELECTED(GetID(),CONTROL_FOREIGNFILTER, CSettings::Get().GetBool("general.addonforeignfilter")); + SET_CONTROL_SELECTED(GetID(),CONTROL_BROKENFILTER, CSettings::Get().GetBool("general.addonbrokenfilter")); + CGUIMediaWindow::UpdateButtons(); +} + +static bool FilterVar(bool valid, const CVariant& variant, + const std::string& check) +{ + if (!valid) + return false; + + if (variant.isNull() || variant.asString().empty()) + return false; + + std::string regions = variant.asString(); + return regions.find(check) == std::string::npos; +} + +bool CGUIWindowAddonBrowser::GetDirectory(const std::string& strDirectory, + CFileItemList& items) +{ + bool result; + if (URIUtils::PathEquals(strDirectory, "addons://downloading/")) + { + VECADDONS addons; + CAddonInstaller::Get().GetInstallList(addons); + + CURL url(strDirectory); + CAddonsDirectory::GenerateListing(url,addons,items); + result = true; + items.SetProperty("reponame",g_localizeStrings.Get(24067)); + items.SetPath(strDirectory); + + if (m_guiState.get() && !m_guiState->HideParentDirItems()) + { + CFileItemPtr pItem(new CFileItem("..")); + pItem->SetPath(m_history.GetParentPath()); + pItem->m_bIsFolder = true; + pItem->m_bIsShareOrDrive = false; + items.AddFront(pItem, 0); + } + + } + else + { + result = CGUIMediaWindow::GetDirectory(strDirectory,items); + if (CSettings::Get().GetBool("general.addonforeignfilter")) + { + int i=0; + while (i < items.Size()) + { + if (!FilterVar(true, items[i]->GetProperty("Addon.Language"), "en") || + !FilterVar(true, items[i]->GetProperty("Addon.Language"), g_langInfo.GetLanguageLocale())) + { + i++; + } + else + items.Remove(i); + } + } + if (CSettings::Get().GetBool("general.addonbrokenfilter")) + { + for (int i = items.Size() - 1; i >= 0; i--) + { + if (!items[i]->GetProperty("Addon.Broken").empty()) + { //check if it's installed + AddonPtr addon; + if (!CAddonMgr::Get().GetAddon(items[i]->GetProperty("Addon.ID").asString(), addon)) + items.Remove(i); + } + } + } + } + + if (strDirectory.empty() && CAddonInstaller::Get().IsDownloading()) + { + CFileItemPtr item(new CFileItem("addons://downloading/",true)); + item->SetLabel(g_localizeStrings.Get(24067)); + item->SetLabelPreformated(true); + item->SetIconImage("DefaultNetwork.png"); + items.Add(item); + } + + items.SetContent("addons"); + + for (int i=0;im_bIsFolder) return; + unsigned int percent; + if (CAddonInstaller::Get().GetProgress(item->GetProperty("Addon.ID").asString(), percent)) + { + std::string progress = StringUtils::Format(g_localizeStrings.Get(24042).c_str(), percent); + item->SetProperty("Addon.Status", progress); + item->SetProperty("Addon.Downloading", true); + } + else + item->ClearProperty("Addon.Downloading"); + item->SetLabel2(item->GetProperty("Addon.Status").asString()); + // to avoid the view state overriding label 2 + item->SetLabelPreformated(true); +} + +bool CGUIWindowAddonBrowser::Update(const std::string &strDirectory, bool updateFilterPath /* = true */) +{ + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + + if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath)) + return false; + + m_thumbLoader.Load(*m_vecItems); + + return true; +} + +int CGUIWindowAddonBrowser::SelectAddonID(TYPE type, std::string &addonID, bool showNone /* = false */, bool showDetails /* = true */, bool showInstalled /* = true */, bool showInstallable /*= false */, bool showMore /* = true */) +{ + vector types; + types.push_back(type); + return SelectAddonID(types, addonID, showNone, showDetails, showInstalled, showInstallable, showMore); +} + +int CGUIWindowAddonBrowser::SelectAddonID(ADDON::TYPE type, vector &addonIDs, bool showNone /* = false */, bool showDetails /* = true */, bool multipleSelection /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) +{ + vector types; + types.push_back(type); + return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, showInstalled, showInstallable, showMore); +} + +int CGUIWindowAddonBrowser::SelectAddonID(const vector &types, std::string &addonID, bool showNone /* = false */, bool showDetails /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) +{ + vector addonIDs; + if (!addonID.empty()) + addonIDs.push_back(addonID); + int retval = SelectAddonID(types, addonIDs, showNone, showDetails, false, showInstalled, showInstallable, showMore); + if (addonIDs.size() > 0) + addonID = addonIDs.at(0); + else + addonID = ""; + return retval; +} + +int CGUIWindowAddonBrowser::SelectAddonID(const vector &types, vector &addonIDs, bool showNone /* = false */, bool showDetails /* = true */, bool multipleSelection /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) +{ + // if we shouldn't show neither installed nor installable addons the list will be empty + if (!showInstalled && !showInstallable) + return 0; + + // can't show the "Get More" button if we already show installable addons + if (showInstallable) + showMore = false; + + CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + if (!dialog) + return 0; + + // get rid of any invalid addon types + vector validTypes(types.size()); + std::copy_if(types.begin(), types.end(), validTypes.begin(), [](ADDON::TYPE type) { return type != ADDON_UNKNOWN; }); + + if (validTypes.empty()) + return 0; + + // get all addons to show + VECADDONS addons; + if (showInstalled) + { + for (vector::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) + { + VECADDONS typeAddons; + if (*type == ADDON_AUDIO) + CAddonsDirectory::GetScriptsAndPlugins("audio", typeAddons); + else if (*type == ADDON_EXECUTABLE) + CAddonsDirectory::GetScriptsAndPlugins("executable", typeAddons); + else if (*type == ADDON_IMAGE) + CAddonsDirectory::GetScriptsAndPlugins("image", typeAddons); + else if (*type == ADDON_VIDEO) + CAddonsDirectory::GetScriptsAndPlugins("video", typeAddons); + else + CAddonMgr::Get().GetAddons(*type, typeAddons); + + addons.insert(addons.end(), typeAddons.begin(), typeAddons.end()); + } + } + + if (showInstallable || showMore) + { + VECADDONS installableAddons; + CAddonDatabase database; + if (database.Open() && database.GetAddons(installableAddons)) + { + for (ADDON::IVECADDONS addon = installableAddons.begin(); addon != installableAddons.end();) + { + AddonPtr pAddon = *addon; + + // check if the addon matches one of the provided addon types + bool matchesType = false; + for (vector::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) + { + if (pAddon->IsType(*type)) + { + matchesType = true; + break; + } + } + + // only show addons that match one of the provided addon types and that aren't disabled + if (matchesType && !CAddonMgr::Get().IsAddonDisabled(pAddon->ID())) + { + // check if the addon is installed + bool isInstalled = CAddonMgr::Get().IsAddonInstalled(pAddon->ID()); + + // check if the addon is installed or can be installed + if ((showInstallable || showMore) && !isInstalled && CAddonMgr::Get().CanAddonBeInstalled(pAddon)) + { + ++addon; + continue; + } + } + + addon = installableAddons.erase(addon); + } + + if (showInstallable) + addons.insert(addons.end(), installableAddons.begin(), installableAddons.end()); + else if (showMore) + showMore = !installableAddons.empty(); + } + } + + if (addons.empty() && !showNone) + return 0; + + // turn the addons into items + std::map addonMap; + CFileItemList items; + for (ADDON::IVECADDONS addon = addons.begin(); addon != addons.end(); ++addon) + { + CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*addon, "")); + if (!items.Contains(item->GetPath())) + { + items.Add(item); + addonMap.insert(std::make_pair(item->GetPath(), *addon)); + } + } + + if (items.IsEmpty() && !showNone) + return 0; + + std::string heading; + for (vector::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) + { + if (!heading.empty()) + heading += ", "; + heading += TranslateType(*type, true); + } + + dialog->SetHeading(heading); + dialog->Reset(); + dialog->SetUseDetails(showDetails); + + if (multipleSelection) + { + showNone = false; + showMore = false; + dialog->EnableButton(true, 186); + } + else if (showMore) + dialog->EnableButton(true, 21452); + + if (showNone) + { + CFileItemPtr item(new CFileItem("", false)); + item->SetLabel(g_localizeStrings.Get(231)); + item->SetLabel2(g_localizeStrings.Get(24040)); + item->SetIconImage("DefaultAddonNone.png"); + item->SetSpecialSort(SortSpecialOnTop); + items.Add(item); + } + items.Sort(SortByLabel, SortOrderAscending); + + if (addonIDs.size() > 0) + { + for (vector::const_iterator it = addonIDs.begin(); it != addonIDs.end() ; ++it) + { + CFileItemPtr item = items.Get(*it); + if (item) + item->Select(true); + } + } + dialog->SetItems(&items); + dialog->SetMultiSelection(multipleSelection); + dialog->DoModal(); + + // if the "Get More" button has been pressed and we haven't shown the + // installable addons so far show a list of installable addons + if (showMore&& dialog->IsButtonPressed()) + return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, false, true, false); + + if (!dialog->IsConfirmed()) + return 0; + + addonIDs.clear(); + const CFileItemList& list = dialog->GetSelectedItems(); + for (int i = 0 ; i < list.Size() ; i++) + { + const CFileItemPtr& item = list.Get(i); + + // check if one of the selected addons needs to be installed + if (showInstallable) + { + std::map::const_iterator itAddon = addonMap.find(item->GetPath()); + if (itAddon != addonMap.end()) + { + const AddonPtr& addon = itAddon->second; + + // if the addon isn't installed we need to install it + if (!CAddonMgr::Get().IsAddonInstalled(addon->ID())) + { + AddonPtr installedAddon; + if (!CAddonInstaller::Get().InstallModal(addon->ID(), installedAddon, false)) + continue; + } + + // if the addon is disabled we need to enable it + if (CAddonMgr::Get().IsAddonDisabled(addon->ID())) + CAddonMgr::Get().DisableAddon(addon->ID(), false); + } + } + + addonIDs.push_back(item->GetPath()); + } + return 1; +} + +std::string CGUIWindowAddonBrowser::GetStartFolder(const std::string &dir) +{ + if (URIUtils::PathStarts(dir, "addons://")) + return dir; + return CGUIMediaWindow::GetStartFolder(dir); +} diff --git a/xbmc/addons/GUIWindowAddonBrowser.h b/xbmc/addons/GUIWindowAddonBrowser.h new file mode 100644 index 0000000..d7c0ee0 --- /dev/null +++ b/xbmc/addons/GUIWindowAddonBrowser.h @@ -0,0 +1,79 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "addons/Addon.h" +#include "windows/GUIMediaWindow.h" +#include "ThumbLoader.h" + +class CFileItem; +class CFileItemList; + +class CGUIWindowAddonBrowser : public CGUIMediaWindow +{ +public: + CGUIWindowAddonBrowser(void); + virtual ~CGUIWindowAddonBrowser(void); + virtual bool OnMessage(CGUIMessage& message); + + /*! \brief Popup a selection dialog with a list of addons of the given type + \param type the type of addon wanted + \param addonID [out] the addon ID of the selected item + \param showNone whether there should be a "None" item in the list (defaults to false) + \param showDetails whether to show details of the addons or not + \param showInstalled whether installed addons should be in the list + \param showInstallable whether installable addons should be in the list + \param showMore whether to show the "Get More" button (only makes sense if showInstalled is true and showInstallable is false) + \return 1 if an addon was selected, 2 if "Get More" was chosen, or 0 if an error occurred or if the selection process was cancelled + */ + static int SelectAddonID(ADDON::TYPE type, std::string &addonID, bool showNone = false, bool showDetails = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + static int SelectAddonID(const std::vector &types, std::string &addonID, bool showNone = false, bool showDetails = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + /*! \brief Popup a selection dialog with a list of addons of the given type + \param type the type of addon wanted + \param addonIDs [out] array of selected addon IDs + \param showNone whether there should be a "None" item in the list (defaults to false) + \param showDetails whether to show details of the addons or not + \param multipleSelection allow selection of multiple addons, if set to true showNone will automaticly switch to false + \param showInstalled whether installed addons should be in the list + \param showInstallable whether installable addons should be in the list + \param showMore whether to show the "Get More" button (only makes sense if showInstalled is true and showInstallable is false) + \return 1 if an addon was selected or multiple selection was specified, 2 if "Get More" was chosen, or 0 if an error occurred or if the selection process was cancelled + */ + static int SelectAddonID(ADDON::TYPE type, std::vector &addonIDs, bool showNone = false, bool showDetails = true, bool multipleSelection = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + static int SelectAddonID(const std::vector &types, std::vector &addonIDs, bool showNone = false, bool showDetails = true, bool multipleSelection = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + +protected: + /* \brief set label2 of an item based on the Addon.Status property + \param item the item to update + */ + void SetItemLabel2(CFileItemPtr item); + + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons); + virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + virtual bool OnClick(int iItem); + virtual void UpdateButtons(); + virtual bool GetDirectory(const std::string &strDirectory, CFileItemList &items); + virtual bool Update(const std::string &strDirectory, bool updateFilterPath = true); + virtual std::string GetStartFolder(const std::string &dir); +private: + CProgramThumbLoader m_thumbLoader; +}; + diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h new file mode 100644 index 0000000..76f22fa --- /dev/null +++ b/xbmc/addons/IAddon.h @@ -0,0 +1,138 @@ +#pragma once +/* +* Copyright (C) 2005-2013 Team XBMC +* http://xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, see +* . +* +*/ +#include + +#include +#include +#include +#include + +class TiXmlElement; + +namespace ADDON +{ + typedef enum + { + ADDON_UNKNOWN, + ADDON_VIZ, + ADDON_SKIN, + ADDON_PVRDLL, + ADDON_SCRIPT, + ADDON_SCRIPT_WEATHER, + ADDON_SUBTITLE_MODULE, + ADDON_SCRIPT_LYRICS, + ADDON_SCRAPER_ALBUMS, + ADDON_SCRAPER_ARTISTS, + ADDON_SCRAPER_MOVIES, + ADDON_SCRAPER_MUSICVIDEOS, + ADDON_SCRAPER_TVSHOWS, + ADDON_SCREENSAVER, + ADDON_PLUGIN, + ADDON_REPOSITORY, + ADDON_WEB_INTERFACE, + ADDON_SERVICE, + ADDON_AUDIOENCODER, + ADDON_CONTEXT_ITEM, + ADDON_VIDEO, // virtual addon types + ADDON_AUDIO, + ADDON_IMAGE, + ADDON_EXECUTABLE, + ADDON_VIZ_LIBRARY, + ADDON_SCRAPER_LIBRARY, + ADDON_SCRIPT_LIBRARY, + ADDON_SCRIPT_MODULE, + ADDON_MAX + } TYPE; + + class IAddon; + typedef std::shared_ptr AddonPtr; + class CVisualisation; + typedef std::shared_ptr VizPtr; + class CSkinInfo; + typedef std::shared_ptr SkinPtr; + class CPluginSource; + typedef std::shared_ptr PluginPtr; + + class CAddonMgr; + class AddonVersion; + typedef std::map > ADDONDEPS; + typedef std::map InfoMap; + class AddonProps; + + class IAddon : public std::enable_shared_from_this + { + public: + virtual ~IAddon() {}; + virtual AddonPtr Clone() const =0; + virtual TYPE Type() const =0; + virtual bool IsType(TYPE type) const =0; + virtual AddonProps Props() const =0; + virtual AddonProps& Props() =0; + virtual const std::string ID() const =0; + virtual const std::string Name() const =0; + virtual bool Enabled() const =0; + virtual bool IsInUse() const =0; + virtual const AddonVersion Version() const =0; + virtual const AddonVersion MinVersion() const =0; + virtual const std::string Summary() const =0; + virtual const std::string Description() const =0; + virtual const std::string Path() const =0; + virtual const std::string Profile() const =0; + virtual const std::string LibPath() const =0; + virtual const std::string ChangeLog() const =0; + virtual const std::string FanArt() const =0; + virtual const std::string Author() const =0; + virtual const std::string Icon() const =0; + virtual int Stars() const =0; + virtual const std::string Disclaimer() const =0; + virtual const InfoMap &ExtraInfo() const =0; + virtual bool HasSettings() =0; + virtual void SaveSettings() =0; + virtual void UpdateSetting(const std::string& key, const std::string& value) =0; + virtual std::string GetSetting(const std::string& key) =0; + virtual TiXmlElement* GetSettingsXML() =0; + virtual std::string GetString(uint32_t id) =0; + virtual const ADDONDEPS &GetDeps() const =0; + virtual AddonVersion GetDependencyVersion(const std::string &dependencyID) const =0; + virtual bool MeetsVersion(const AddonVersion &version) const =0; + virtual bool ReloadSettings() =0; + virtual void OnDisabled() =0; + virtual void OnEnabled() =0; + virtual AddonPtr GetRunningInstance() const=0; + virtual bool OnPreInstall() =0; + virtual void OnPostInstall(bool restart, bool update, bool modal) =0; + virtual void OnPreUnInstall() =0; + virtual void OnPostUnInstall() =0; + virtual bool CanInstall(const std::string& referer) =0; + + protected: + virtual bool LoadSettings(bool bForce = false) =0; + + private: + friend class CAddonMgr; + virtual bool IsAddonLibrary() =0; + virtual void Enable() =0; + virtual void Disable() =0; + virtual bool LoadStrings() =0; + virtual void ClearStrings() =0; + }; +}; + diff --git a/xbmc/addons/Makefile b/xbmc/addons/Makefile new file mode 100644 index 0000000..4cf5b17 --- /dev/null +++ b/xbmc/addons/Makefile @@ -0,0 +1,31 @@ +SRCS=Addon.cpp \ + AddonCallbacks.cpp \ + AddonCallbacksAddon.cpp \ + AddonCallbacksCodec.cpp \ + AddonCallbacksGUI.cpp \ + AddonCallbacksPVR.cpp \ + AddonDatabase.cpp \ + AddonInstaller.cpp \ + AddonManager.cpp \ + AddonStatusHandler.cpp \ + AddonVersion.cpp \ + AudioEncoder.cpp \ + ContextItemAddon.cpp \ + GUIDialogAddonInfo.cpp \ + GUIDialogAddonSettings.cpp \ + GUIViewStateAddonBrowser.cpp \ + GUIWindowAddonBrowser.cpp \ + PluginSource.cpp \ + Repository.cpp \ + Scraper.cpp \ + ScreenSaver.cpp \ + Service.cpp \ + Skin.cpp \ + Visualisation.cpp \ + Webinterface.cpp \ + +LIB=addons.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + diff --git a/xbmc/addons/PluginSource.cpp b/xbmc/addons/PluginSource.cpp new file mode 100644 index 0000000..a5729cf --- /dev/null +++ b/xbmc/addons/PluginSource.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "PluginSource.h" +#include "AddonManager.h" +#include "utils/StringUtils.h" + +using namespace std; + +namespace ADDON +{ + +CPluginSource::CPluginSource(const AddonProps &props) + : CAddon(props) +{ + std::string provides; + InfoMap::const_iterator i = Props().extrainfo.find("provides"); + if (i != Props().extrainfo.end()) + provides = i->second; + SetProvides(provides); +} + +CPluginSource::CPluginSource(const cp_extension_t *ext) + : CAddon(ext) +{ + std::string provides; + if (ext) + { + provides = CAddonMgr::Get().GetExtValue(ext->configuration, "provides"); + if (!provides.empty()) + Props().extrainfo.insert(make_pair("provides", provides)); + } + SetProvides(provides); +} + +AddonPtr CPluginSource::Clone() const +{ + return AddonPtr(new CPluginSource(*this)); +} + +void CPluginSource::SetProvides(const std::string &content) +{ + if (!content.empty()) + { + vector provides = StringUtils::Split(content, ' '); + for (vector::const_iterator i = provides.begin(); i != provides.end(); ++i) + { + Content content = Translate(*i); + if (content != UNKNOWN) + m_providedContent.insert(content); + } + } + if (Type() == ADDON_SCRIPT && m_providedContent.empty()) + m_providedContent.insert(EXECUTABLE); +} + +CPluginSource::Content CPluginSource::Translate(const std::string &content) +{ + if (content == "audio") + return CPluginSource::AUDIO; + else if (content == "image") + return CPluginSource::IMAGE; + else if (content == "executable") + return CPluginSource::EXECUTABLE; + else if (content == "video") + return CPluginSource::VIDEO; + else + return CPluginSource::UNKNOWN; +} + +bool CPluginSource::IsType(TYPE type) const +{ + return ((type == ADDON_VIDEO && Provides(VIDEO)) + || (type == ADDON_AUDIO && Provides(AUDIO)) + || (type == ADDON_IMAGE && Provides(IMAGE)) + || (type == ADDON_EXECUTABLE && Provides(EXECUTABLE))); +} + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/PluginSource.h b/xbmc/addons/PluginSource.h new file mode 100644 index 0000000..87e1f34 --- /dev/null +++ b/xbmc/addons/PluginSource.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "Addon.h" + +namespace ADDON +{ + +class CPluginSource : public CAddon +{ +public: + + enum Content { UNKNOWN, AUDIO, IMAGE, EXECUTABLE, VIDEO }; + + CPluginSource(const cp_extension_t *ext); + CPluginSource(const AddonProps &props); + virtual ~CPluginSource() {} + virtual AddonPtr Clone() const; + virtual bool IsType(TYPE type) const; + bool Provides(const Content& content) const + { + return content == UNKNOWN ? false : m_providedContent.count(content) > 0; + } + + bool ProvidesSeveral() const + { + return m_providedContent.size() > 1; + } + + static Content Translate(const std::string &content); +private: + /*! \brief Set the provided content for this plugin + If no valid content types are passed in, we set the EXECUTABLE type + \param content a space-separated list of content types + */ + void SetProvides(const std::string &content); + std::set m_providedContent; +}; + +} /*namespace ADDON*/ diff --git a/xbmc/addons/Repository.cpp b/xbmc/addons/Repository.cpp new file mode 100644 index 0000000..e65a195 --- /dev/null +++ b/xbmc/addons/Repository.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Repository.h" +#include "addons/AddonDatabase.h" +#include "addons/AddonInstaller.h" +#include "addons/AddonManager.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "filesystem/File.h" +#include "filesystem/PluginDirectory.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/JobManager.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/XBMCTinyXML.h" +#include "FileItem.h" +#include "TextureDatabase.h" +#include "URL.h" + +using namespace std; +using namespace XFILE; +using namespace ADDON; + +AddonPtr CRepository::Clone() const +{ + return AddonPtr(new CRepository(*this)); +} + +CRepository::CRepository(const AddonProps& props) : + CAddon(props) +{ +} + +CRepository::CRepository(const cp_extension_t *ext) + : CAddon(ext) +{ + // read in the other props that we need + if (ext) + { + AddonVersion version("0.0.0"); + AddonPtr addonver; + if (CAddonMgr::Get().GetAddon("xbmc.addon", addonver)) + version = addonver->Version(); + for (size_t i = 0; i < ext->configuration->num_children; ++i) + { + if(ext->configuration->children[i].name && + strcmp(ext->configuration->children[i].name, "dir") == 0) + { + AddonVersion min_version(CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "@minversion")); + if (min_version <= version) + { + DirInfo dir; + dir.version = min_version; + dir.checksum = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "checksum"); + dir.compressed = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "info@compressed") == "true"; + dir.info = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "info"); + dir.datadir = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "datadir"); + dir.zipped = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "datadir@zip") == "true"; + dir.hashes = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "hashes") == "true"; + m_dirs.push_back(dir); + } + } + } + // backward compatibility + if (!CAddonMgr::Get().GetExtValue(ext->configuration, "info").empty()) + { + DirInfo info; + info.checksum = CAddonMgr::Get().GetExtValue(ext->configuration, "checksum"); + info.compressed = CAddonMgr::Get().GetExtValue(ext->configuration, "info@compressed") == "true"; + info.info = CAddonMgr::Get().GetExtValue(ext->configuration, "info"); + info.datadir = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir"); + info.zipped = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir@zip") == "true"; + info.hashes = CAddonMgr::Get().GetExtValue(ext->configuration, "hashes") == "true"; + m_dirs.push_back(info); + } + } +} + +CRepository::CRepository(const CRepository &rhs) + : CAddon(rhs), m_dirs(rhs.m_dirs) +{ +} + +CRepository::~CRepository() +{ +} + +string CRepository::FetchChecksum(const string& url) +{ + CFile file; + try + { + if (file.Open(url)) + { + // we intentionally avoid using file.GetLength() for + // Transfer-Encoding: chunked servers. + std::stringstream str; + char temp[1024]; + int read; + while ((read=file.Read(temp, sizeof(temp))) > 0) + str.write(temp, read); + return str.str(); + } + return ""; + } + catch (...) + { + return ""; + } +} + +string CRepository::GetAddonHash(const AddonPtr& addon) const +{ + string checksum; + DirList::const_iterator it; + for (it = m_dirs.begin();it != m_dirs.end(); ++it) + if (URIUtils::IsInPath(addon->Path(), it->datadir)) + break; + if (it != m_dirs.end() && it->hashes) + { + checksum = FetchChecksum(addon->Path()+".md5"); + size_t pos = checksum.find_first_of(" \n"); + if (pos != string::npos) + return checksum.substr(0, pos); + } + return checksum; +} + +#define SET_IF_NOT_EMPTY(x,y) \ + { \ + if (!x.empty()) \ + x = y; \ + } + +bool CRepository::Parse(const DirInfo& dir, VECADDONS &result) +{ + string file = dir.info; + if (dir.compressed) + { + CURL url(dir.info); + string opts = url.GetProtocolOptions(); + if (!opts.empty()) + opts += "&"; + url.SetProtocolOptions(opts+"Encoding=gzip"); + file = url.Get(); + } + + CXBMCTinyXML doc; + if (doc.LoadFile(file) && doc.RootElement() && + CAddonMgr::Get().AddonsFromRepoXML(doc.RootElement(), result)) + { + for (IVECADDONS i = result.begin(); i != result.end(); ++i) + { + AddonPtr addon = *i; + if (dir.zipped) + { + string file = StringUtils::Format("%s/%s-%s.zip", addon->ID().c_str(), addon->ID().c_str(), addon->Version().asString().c_str()); + addon->Props().path = URIUtils::AddFileToFolder(dir.datadir,file); + SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/icon.png")) + file = StringUtils::Format("%s/changelog-%s.txt", addon->ID().c_str(), addon->Version().asString().c_str()); + SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(dir.datadir,file)) + SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/fanart.jpg")) + } + else + { + addon->Props().path = URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/"); + SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/icon.png")) + SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/changelog.txt")) + SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/fanart.jpg")) + } + } + return true; + } + return false; +} + +void CRepository::OnPostInstall(bool restart, bool update, bool modal) +{ + VECADDONS addons; + AddonPtr repo(new CRepository(*this)); + addons.push_back(repo); + CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), &CAddonInstaller::Get()); +} + +void CRepository::OnPostUnInstall() +{ + CAddonDatabase database; + database.Open(); + database.DeleteRepository(ID()); +} + +CRepositoryUpdateJob::CRepositoryUpdateJob(const VECADDONS &repos) + : m_repos(repos) +{ +} + +void MergeAddons(map &addons, const VECADDONS &new_addons) +{ + for (VECADDONS::const_iterator it = new_addons.begin(); it != new_addons.end(); ++it) + { + map::iterator existing = addons.find((*it)->ID()); + if (existing != addons.end()) + { // already got it - replace if we have a newer version + if (existing->second->Version() < (*it)->Version()) + existing->second = *it; + } + else + addons.insert(make_pair((*it)->ID(), *it)); + } +} + +bool CRepositoryUpdateJob::DoWork() +{ + map addons; + for (VECADDONS::const_iterator i = m_repos.begin(); i != m_repos.end(); ++i) + { + if (ShouldCancel(0, 0)) + return false; + const RepositoryPtr repo = std::dynamic_pointer_cast(*i); + VECADDONS newAddons; + if (GrabAddons(repo, newAddons)) + MergeAddons(addons, newAddons); + } + if (addons.empty()) + return true; //Nothing to do + + // check for updates + CAddonDatabase database; + database.Open(); + database.BeginMultipleExecute(); + + CTextureDatabase textureDB; + textureDB.Open(); + textureDB.BeginMultipleExecute(); + VECADDONS notifications; + for (map::const_iterator i = addons.begin(); i != addons.end(); ++i) + { + // manager told us to feck off + if (ShouldCancel(0,0)) + break; + + AddonPtr newAddon = i->second; + bool deps_met = CAddonInstaller::Get().CheckDependencies(newAddon, &database); + if (!deps_met && newAddon->Props().broken.empty()) + newAddon->Props().broken = "DEPSNOTMET"; + + // invalidate the art associated with this item + if (!newAddon->Props().fanart.empty()) + textureDB.InvalidateCachedTexture(newAddon->Props().fanart); + if (!newAddon->Props().icon.empty()) + textureDB.InvalidateCachedTexture(newAddon->Props().icon); + + AddonPtr addon; + CAddonMgr::Get().GetAddon(newAddon->ID(),addon); + if (addon && newAddon->Version() > addon->Version() && + !database.IsAddonBlacklisted(newAddon->ID(),newAddon->Version().asString()) && + deps_met) + { + if (CSettings::Get().GetInt("general.addonupdates") == AUTO_UPDATES_ON) + { + string referer; + if (URIUtils::IsInternetStream(newAddon->Path())) + referer = StringUtils::Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().asString().c_str()); + + if (newAddon->CanInstall(referer)) + CAddonInstaller::Get().Install(addon->ID(), true, referer); + } + else + notifications.push_back(addon); + } + + // Check if we should mark the add-on as broken. We may have a newer version + // of this add-on in the database or installed - if so, we keep it unbroken. + bool haveNewer = (addon && addon->Version() > newAddon->Version()) || + database.GetAddonVersion(newAddon->ID()) > newAddon->Version(); + if (!haveNewer) + { + if (!newAddon->Props().broken.empty()) + { + if (database.IsAddonBroken(newAddon->ID()).empty()) + { + std::string line = g_localizeStrings.Get(24096); + if (newAddon->Props().broken == "DEPSNOTMET") + line = g_localizeStrings.Get(24104); + if (addon && CGUIDialogYesNo::ShowAndGetInput(newAddon->Name(), + line, + g_localizeStrings.Get(24097), + "")) + CAddonMgr::Get().DisableAddon(newAddon->ID()); + } + } + database.BreakAddon(newAddon->ID(), newAddon->Props().broken); + } + } + database.CommitMultipleExecute(); + textureDB.CommitMultipleExecute(); + if (!notifications.empty() && CSettings::Get().GetBool("general.addonnotifications")) + { + if (notifications.size() == 1) + CGUIDialogKaiToast::QueueNotification(notifications[0]->Icon(), + g_localizeStrings.Get(24061), + notifications[0]->Name(),TOAST_DISPLAY_TIME,false,TOAST_DISPLAY_TIME); + else + CGUIDialogKaiToast::QueueNotification("", + g_localizeStrings.Get(24001), + g_localizeStrings.Get(24061),TOAST_DISPLAY_TIME,false,TOAST_DISPLAY_TIME); + } + + return true; +} + +bool CRepositoryUpdateJob::GrabAddons(const RepositoryPtr& repo, VECADDONS& addons) +{ + CAddonDatabase database; + database.Open(); + string oldReposum; + if (!database.GetRepoChecksum(repo->ID(), oldReposum)) + oldReposum = ""; + + string reposum; + for (CRepository::DirList::const_iterator it = repo->m_dirs.begin(); it != repo->m_dirs.end(); ++it) + { + if (ShouldCancel(0, 0)) + return false; + if (!it->checksum.empty()) + { + const string dirsum = CRepository::FetchChecksum(it->checksum); + if (dirsum.empty()) + { + CLog::Log(LOGERROR, "Failed to fetch checksum for directory listing %s for repository %s. ", (*it).info.c_str(), repo->ID().c_str()); + return false; + } + reposum += dirsum; + } + } + + if (oldReposum != reposum || oldReposum.empty()) + { + map uniqueAddons; + for (CRepository::DirList::const_iterator it = repo->m_dirs.begin(); it != repo->m_dirs.end(); ++it) + { + if (ShouldCancel(0, 0)) + return false; + VECADDONS addons; + if (!CRepository::Parse(*it, addons)) + { //TODO: Hash is invalid and should not be saved, but should we fail? + //We can still report a partial addon listing. + CLog::Log(LOGERROR, "Failed to read directory listing %s for repository %s. ", (*it).info.c_str(), repo->ID().c_str()); + return false; + } + MergeAddons(uniqueAddons, addons); + } + + bool add = true; + if (!repo->Props().libname.empty()) + { + CFileItemList dummy; + string s = StringUtils::Format("plugin://%s/?action=update", repo->ID().c_str()); + add = CDirectory::GetDirectory(s, dummy); + } + if (add) + { + for (map::const_iterator i = uniqueAddons.begin(); i != uniqueAddons.end(); ++i) + addons.push_back(i->second); + database.AddRepository(repo->ID(), addons, reposum, repo->Version()); + } + } + else + { + CLog::Log(LOGDEBUG, "Checksum for repository %s not changed.", repo->ID().c_str()); + database.GetRepository(repo->ID(), addons); + database.SetRepoTimestamp(repo->ID(), CDateTime::GetCurrentDateTime().GetAsDBDateTime(), repo->Version()); + } + return true; +} + diff --git a/xbmc/addons/Repository.h b/xbmc/addons/Repository.h new file mode 100644 index 0000000..5120c38 --- /dev/null +++ b/xbmc/addons/Repository.h @@ -0,0 +1,82 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Addon.h" +#include "utils/Job.h" + +namespace ADDON +{ + class CRepository; + typedef std::shared_ptr RepositoryPtr; + class CRepository : public CAddon + { + public: + virtual AddonPtr Clone() const; + CRepository(const AddonProps& props); + CRepository(const cp_extension_t *props); + virtual ~CRepository(); + + /*! \brief Get the md5 hash for an addon. + \param the addon in question. + \return the md5 hash for the given addon, empty if non exists. + */ + std::string GetAddonHash(const AddonPtr& addon) const; + + struct DirInfo + { + DirInfo() : version("0.0.0"), compressed(false), zipped(false), hashes(false) {} + AddonVersion version; + std::string info; + std::string checksum; + std::string datadir; + bool compressed; + bool zipped; + bool hashes; + }; + + typedef std::vector DirList; + DirList m_dirs; + + static bool Parse(const DirInfo& dir, VECADDONS& addons); + static std::string FetchChecksum(const std::string& url); + + virtual void OnPostInstall(bool restart, bool update, bool modal); + virtual void OnPostUnInstall(); + + private: + CRepository(const CRepository &rhs); + }; + + class CRepositoryUpdateJob : public CJob + { + public: + CRepositoryUpdateJob(const VECADDONS& repos); + virtual ~CRepositoryUpdateJob() {} + + virtual const char *GetType() const { return "repoupdate"; }; + virtual bool DoWork(); + private: + bool GrabAddons(const RepositoryPtr& repo, VECADDONS& addons); + + VECADDONS m_repos; + }; +} + diff --git a/xbmc/addons/Scraper.cpp b/xbmc/addons/Scraper.cpp new file mode 100644 index 0000000..06f34f2 --- /dev/null +++ b/xbmc/addons/Scraper.cpp @@ -0,0 +1,1033 @@ +/* +* Copyright (C) 2005-2013 Team XBMC +* http://xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, see +* . +* +*/ +#include "Scraper.h" +#include "filesystem/File.h" +#include "filesystem/Directory.h" +#include "filesystem/CurlFile.h" +#include "AddonManager.h" +#include "utils/ScraperParser.h" +#include "utils/ScraperUrl.h" +#include "utils/CharsetConverter.h" +#include "utils/log.h" +#include "music/infoscanner/MusicAlbumInfo.h" +#include "music/infoscanner/MusicArtistInfo.h" +#include "utils/fstrcmp.h" +#include "settings/AdvancedSettings.h" +#include "FileItem.h" +#include "utils/URIUtils.h" +#include "utils/XMLUtils.h" +#include "utils/StringUtils.h" +#include "music/MusicDatabase.h" +#include "video/VideoDatabase.h" +#include "music/Album.h" +#include "music/Artist.h" +#include "Util.h" +#include "URL.h" + +#include +#include + +using namespace std; +using namespace XFILE; +using namespace MUSIC_GRABBER; +using namespace VIDEO; + +namespace ADDON +{ + +typedef struct +{ + const char* name; + CONTENT_TYPE type; + int pretty; +} ContentMapping; + +static const ContentMapping content[] = + {{"unknown", CONTENT_NONE, 231 }, + {"albums", CONTENT_ALBUMS, 132 }, + {"music", CONTENT_ALBUMS, 132 }, + {"artists", CONTENT_ARTISTS, 133 }, + {"movies", CONTENT_MOVIES, 20342 }, + {"tvshows", CONTENT_TVSHOWS, 20343 }, + {"musicvideos", CONTENT_MUSICVIDEOS, 20389 }}; + +std::string TranslateContent(const CONTENT_TYPE &type, bool pretty/*=false*/) +{ + for (unsigned int index=0; index < ARRAY_SIZE(content); ++index) + { + const ContentMapping &map = content[index]; + if (type == map.type) + { + if (pretty && map.pretty) + return g_localizeStrings.Get(map.pretty); + else + return map.name; + } + } + return ""; +} + +CONTENT_TYPE TranslateContent(const std::string &string) +{ + for (unsigned int index=0; index < ARRAY_SIZE(content); ++index) + { + const ContentMapping &map = content[index]; + if (string == map.name) + return map.type; + } + return CONTENT_NONE; +} + +TYPE ScraperTypeFromContent(const CONTENT_TYPE &content) +{ + switch (content) + { + case CONTENT_ALBUMS: + return ADDON_SCRAPER_ALBUMS; + case CONTENT_ARTISTS: + return ADDON_SCRAPER_ARTISTS; + case CONTENT_MOVIES: + return ADDON_SCRAPER_MOVIES; + case CONTENT_MUSICVIDEOS: + return ADDON_SCRAPER_MUSICVIDEOS; + case CONTENT_TVSHOWS: + return ADDON_SCRAPER_TVSHOWS; + default: + return ADDON_UNKNOWN; + } +} + +// if the XML root is , throw CScraperError with enclosed /<message> values +static void CheckScraperError(const TiXmlElement *pxeRoot) +{ + if (!pxeRoot || stricmp(pxeRoot->Value(), "error")) + return; + std::string sTitle; + std::string sMessage; + XMLUtils::GetString(pxeRoot, "title", sTitle); + XMLUtils::GetString(pxeRoot, "message", sMessage); + throw CScraperError(sTitle, sMessage); +} + +CScraper::CScraper(const cp_extension_t *ext) : CAddon(ext), m_fLoaded(false) +{ + if (ext) + { + m_language = CAddonMgr::Get().GetExtValue(ext->configuration, "@language"); + m_requiressettings = CAddonMgr::Get().GetExtValue(ext->configuration,"@requiressettings") == "true"; + std::string persistence = CAddonMgr::Get().GetExtValue(ext->configuration, "@cachepersistence"); + if (!persistence.empty()) + m_persistence.SetFromTimeString(persistence); + } + switch (Type()) + { + 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: + m_pathContent = CONTENT_NONE; + break; + } +} + +AddonPtr CScraper::Clone() const +{ + return AddonPtr(new CScraper(*this)); +} + +CScraper::CScraper(const CScraper &rhs) + : CAddon(rhs), m_fLoaded(false), + m_language(rhs.m_language), + m_requiressettings(rhs.m_requiressettings), + m_persistence(rhs.m_persistence), + m_pathContent(rhs.m_pathContent) +{ +} + +bool CScraper::Supports(const CONTENT_TYPE &content) const +{ + return Type() == ScraperTypeFromContent(content); +} + +bool CScraper::SetPathSettings(CONTENT_TYPE content, const std::string& xml) +{ + m_pathContent = content; + if (!LoadSettings()) + return false; + + if (xml.empty()) + return true; + + CXBMCTinyXML doc; + doc.Parse(xml); + m_userSettingsLoaded = SettingsFromXML(doc); + + return m_userSettingsLoaded; +} + +std::string CScraper::GetPathSettings() +{ + if (!LoadSettings()) + return ""; + + stringstream stream; + CXBMCTinyXML doc; + SettingsToXML(doc); + if (doc.RootElement()) + stream << *doc.RootElement(); + + return stream.str(); +} + +void CScraper::ClearCache() +{ + std::string strCachePath = URIUtils::AddFileToFolder(g_advancedSettings.m_cachePath, "scrapers"); + + // create scraper cache dir if needed + if (!CDirectory::Exists(strCachePath)) + CDirectory::Create(strCachePath); + + strCachePath = URIUtils::AddFileToFolder(strCachePath, ID()); + URIUtils::AddSlashAtEnd(strCachePath); + + if (CDirectory::Exists(strCachePath)) + { + CFileItemList items; + CDirectory::GetDirectory(strCachePath,items); + for (int i=0;i<items.Size();++i) + { + // wipe cache + if (items[i]->m_dateTime + m_persistence <= CDateTime::GetCurrentDateTime()) + CFile::Delete(items[i]->GetPath()); + } + } + else + CDirectory::Create(strCachePath); +} + +// returns a vector of strings: the first is the XML output by the function; the rest +// is XML output by chained functions, possibly recursively +// the CCurlFile object is passed in so that URL fetches can be canceled from other threads +// throws CScraperError abort on internal failures (e.g., parse errors) +vector<string> CScraper::Run(const std::string& function, + const CScraperUrl& scrURL, + CCurlFile& http, + const vector<string>* extras) +{ + if (!Load()) + throw CScraperError(); + + std::string strXML = InternalRun(function,scrURL,http,extras); + if (strXML.empty()) + { + if (function != "NfoUrl" && function != "ResolveIDToUrl") + CLog::Log(LOGERROR, "%s: Unable to parse web site",__FUNCTION__); + throw CScraperError(); + } + + CLog::Log(LOGDEBUG,"scraper: %s returned %s",function.c_str(),strXML.c_str()); + + CXBMCTinyXML doc; + /* all data was converted to UTF-8 before being processed by scraper */ + doc.Parse(strXML, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML",__FUNCTION__); + throw CScraperError(); + } + + vector<string> result; + result.push_back(strXML); + TiXmlElement* xchain = doc.RootElement()->FirstChildElement(); + // skip children of the root element until <url> or <chain> + while (xchain && strcmp(xchain->Value(),"url") && strcmp(xchain->Value(),"chain")) + xchain = xchain->NextSiblingElement(); + while (xchain) + { + // <chain|url function="...">param</> + const char* szFunction = xchain->Attribute("function"); + if (szFunction) + { + CScraperUrl scrURL2; + vector<string> extras; + // for <chain>, pass the contained text as a parameter; for <url>, as URL content + if (strcmp(xchain->Value(),"chain")==0) + { + if (xchain->FirstChild()) + extras.push_back(xchain->FirstChild()->Value()); + } + else + scrURL2.ParseElement(xchain); + // Fix for empty chains. $$1 would still contain the + // previous value as there is no child of the xml node. + // since $$1 will always either contain the data from an + // url or the parameters to a chain, we can safely clear it here + // to fix this issue + m_parser.m_param[0].clear(); + vector<string> result2 = RunNoThrow(szFunction,scrURL2,http,&extras); + result.insert(result.end(),result2.begin(),result2.end()); + } + xchain = xchain->NextSiblingElement(); + // continue to skip past non-<url> or <chain> elements + while (xchain && strcmp(xchain->Value(),"url") && strcmp(xchain->Value(),"chain")) + xchain = xchain->NextSiblingElement(); + } + + return result; +} + +// just like Run, but returns an empty list instead of throwing in case of error +// don't use in new code; errors should be handled appropriately +vector<string> CScraper::RunNoThrow(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const vector<string>* extras) +{ + vector<string> vcs; + try + { + vcs = Run(function, url, http, extras); + } + catch (const CScraperError &sce) + { + assert(sce.FAborted()); // the only kind we should get + } + return vcs; +} + +std::string CScraper::InternalRun(const std::string& function, + const CScraperUrl& scrURL, + CCurlFile& http, + const vector<string>* extras) +{ + // walk the list of input URLs and fetch each into parser parameters + unsigned int i; + for (i=0;i<scrURL.m_url.size();++i) + { + if (!CScraperUrl::Get(scrURL.m_url[i],m_parser.m_param[i],http,ID()) || m_parser.m_param[i].size() == 0) + return ""; + } + // put the 'extra' parameterts into the parser parameter list too + if (extras) + { + for (unsigned int j=0;j<extras->size();++j) + m_parser.m_param[j+i] = (*extras)[j]; + } + + return m_parser.Parse(function,this); +} + +bool CScraper::Load() +{ + if (m_fLoaded) + return true; + + bool result=m_parser.Load(LibPath()); + if (result) + { + // TODO: this routine assumes that deps are a single level, and assumes the dep is installed. + // 1. Does it make sense to have recursive dependencies? + // 2. Should we be checking the dep versions or do we assume it is ok? + ADDONDEPS deps = GetDeps(); + ADDONDEPS::iterator itr = deps.begin(); + while (itr != deps.end()) + { + if (itr->first == "xbmc.metadata") + { + ++itr; + continue; + } + AddonPtr dep; + + bool bOptional = itr->second.second; + + if (CAddonMgr::Get().GetAddon((*itr).first, dep)) + { + CXBMCTinyXML doc; + if (dep->Type() == ADDON_SCRAPER_LIBRARY && doc.LoadFile(dep->LibPath())) + m_parser.AddDocument(&doc); + } + else + { + if (!bOptional) + { + result = false; + break; + } + } + ++itr; + } + } + + if (!result) + CLog::Log(LOGWARNING, "failed to load scraper XML from %s", LibPath().c_str()); + return m_fLoaded = result; +} + +bool CScraper::IsInUse() const +{ + if (Supports(CONTENT_ALBUMS) || Supports(CONTENT_ARTISTS)) + { // music scraper + CMusicDatabase db; + if (db.Open() && db.ScraperInUse(ID())) + return true; + } + else + { // video scraper + CVideoDatabase db; + if (db.Open() && db.ScraperInUse(ID())) + return true; + } + return false; +} + +bool CScraper::IsNoop() +{ + if (!Load()) + throw CScraperError(); + + return m_parser.IsNoop(); +} + +// pass in contents of .nfo file; returns URL (possibly empty if none found) +// and may populate strId, or throws CScraperError on error +CScraperUrl CScraper::NfoUrl(const std::string &sNfoContent) +{ + CScraperUrl scurlRet; + + if (IsNoop()) + return scurlRet; + + // scraper function takes contents of .nfo file, returns XML (see below) + vector<string> vcsIn; + vcsIn.push_back(sNfoContent); + CScraperUrl scurl; + CCurlFile fcurl; + vector<string> vcsOut = Run("NfoUrl", scurl, fcurl, &vcsIn); + if (vcsOut.empty() || vcsOut[0].empty()) + return scurlRet; + if (vcsOut.size() > 1) + CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__); + + // parse returned XML: either <error> element on error, blank on failure, + // or <url>...</url> or <url>...</url><id>...</id> on success + for (unsigned int i=0; i < vcsOut.size(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(vcsOut[i], TIXML_ENCODING_UTF8); + CheckScraperError(doc.RootElement()); + + if (doc.RootElement()) + { + /* + NOTE: Scrapers might return invalid xml with some loose + elements (eg. '<url>http://some.url</url><id>123</id>'). + Since XMLUtils::GetString() is assuming well formed xml + with start and end-tags we're not able to use it. + Check for the desired Elements instead. + */ + TiXmlElement* pxeUrl=NULL; + TiXmlElement* pId=NULL; + if (!strcmp(doc.RootElement()->Value(),"details")) + { + pxeUrl = doc.RootElement()->FirstChildElement("url"); + pId = doc.RootElement()->FirstChildElement("id"); + } + else + { + pId = doc.FirstChildElement("id"); + pxeUrl = doc.FirstChildElement("url"); + } + if (pId && pId->FirstChild()) + scurlRet.strId = pId->FirstChild()->Value(); + + if (pxeUrl && pxeUrl->Attribute("function")) + continue; + + if (pxeUrl) + scurlRet.ParseElement(pxeUrl); + else if (!strcmp(doc.RootElement()->Value(), "url")) + scurlRet.ParseElement(doc.RootElement()); + else + continue; + break; + } + } + return scurlRet; +} + +CScraperUrl CScraper::ResolveIDToUrl(const std::string& externalID) +{ + CScraperUrl scurlRet; + + // scraper function takes an external ID, returns XML (see below) + vector<string> vcsIn; + vcsIn.push_back(externalID); + CScraperUrl scurl; + CCurlFile fcurl; + vector<string> vcsOut = Run("ResolveIDToUrl", scurl, fcurl, &vcsIn); + if (vcsOut.empty() || vcsOut[0].empty()) + return scurlRet; + if (vcsOut.size() > 1) + CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__); + + // parse returned XML: either <error> element on error, blank on failure, + // or <url>...</url> or <url>...</url><id>...</id> on success + for (unsigned int i=0; i < vcsOut.size(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(vcsOut[i], TIXML_ENCODING_UTF8); + CheckScraperError(doc.RootElement()); + + if (doc.RootElement()) + { + /* + NOTE: Scrapers might return invalid xml with some loose + elements (eg. '<url>http://some.url</url><id>123</id>'). + Since XMLUtils::GetString() is assuming well formed xml + with start and end-tags we're not able to use it. + Check for the desired Elements instead. + */ + TiXmlElement* pxeUrl=NULL; + TiXmlElement* pId=NULL; + if (!strcmp(doc.RootElement()->Value(),"details")) + { + pxeUrl = doc.RootElement()->FirstChildElement("url"); + pId = doc.RootElement()->FirstChildElement("id"); + } + else + { + pId = doc.FirstChildElement("id"); + pxeUrl = doc.FirstChildElement("url"); + } + if (pId && pId->FirstChild()) + scurlRet.strId = pId->FirstChild()->Value(); + + if (pxeUrl && pxeUrl->Attribute("function")) + continue; + + if (pxeUrl) + scurlRet.ParseElement(pxeUrl); + else if (!strcmp(doc.RootElement()->Value(), "url")) + scurlRet.ParseElement(doc.RootElement()); + else + continue; + break; + } + } + return scurlRet; +} + +static bool RelevanceSortFunction(const CScraperUrl &left, const CScraperUrl &right) +{ + return left.relevance > right.relevance; +} + +// fetch list of matching movies sorted by relevance (may be empty); +// throws CScraperError on error; first called with fFirst set, then unset if first try fails +std::vector<CScraperUrl> CScraper::FindMovie(XFILE::CCurlFile &fcurl, const std::string &sMovie, + bool fFirst) +{ + // prepare parameters for URL creation + std::string sTitle, sTitleYear, sYear; + CUtil::CleanString(sMovie, sTitle, sTitleYear, sYear, true/*fRemoveExt*/, fFirst); + + CLog::Log(LOGDEBUG, "%s: Searching for '%s' using %s scraper " + "(path: '%s', content: '%s', version: '%s')", __FUNCTION__, sTitle.c_str(), + Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + std::vector<CScraperUrl> vcscurl; + if (IsNoop()) + return vcscurl; + + if (!fFirst) + StringUtils::Replace(sTitle, '-',' '); + + vector<string> vcsIn(1); + g_charsetConverter.utf8To(SearchStringEncoding(), sTitle, vcsIn[0]); + vcsIn[0] = CURL::Encode(vcsIn[0]); + if (fFirst && !sYear.empty()) + vcsIn.push_back(sYear); + + // request a search URL from the title/filename/etc. + CScraperUrl scurl; + vector<string> vcsOut = Run("CreateSearchUrl", scurl, fcurl, &vcsIn); + if (vcsOut.empty()) + { + CLog::Log(LOGDEBUG, "%s: CreateSearchUrl failed", __FUNCTION__); + throw CScraperError(); + } + scurl.ParseString(vcsOut[0]); + + // do the search, and parse the result into a list + vcsIn.clear(); + vcsIn.push_back(scurl.m_url[0].m_url); + vcsOut = Run("GetSearchResults", scurl, fcurl, &vcsIn); + + bool fSort(true); + std::set<std::string> stsDupeCheck; + bool fResults(false); + for (vector<string>::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + continue; // might have more valid results later + } + + CheckScraperError(doc.RootElement()); + + TiXmlHandle xhDoc(&doc); + TiXmlHandle xhResults = xhDoc.FirstChild("results"); + if (!xhResults.Element()) + continue; + fResults = true; // even if empty + + // we need to sort if returned results don't specify 'sorted="yes"' + if (fSort) + { + const char *sorted = xhResults.Element()->Attribute("sorted"); + if (sorted != NULL) + fSort = !StringUtils::EqualsNoCase(sorted, "yes"); + } + + for (TiXmlElement *pxeMovie = xhResults.FirstChild("entity").Element(); + pxeMovie; pxeMovie = pxeMovie->NextSiblingElement()) + { + CScraperUrl scurlMovie; + TiXmlNode *pxnTitle = pxeMovie->FirstChild("title"); + TiXmlElement *pxeLink = pxeMovie->FirstChildElement("url"); + if (pxnTitle && pxnTitle->FirstChild() && pxeLink && pxeLink->FirstChild()) + { + scurlMovie.strTitle = pxnTitle->FirstChild()->Value(); + XMLUtils::GetString(pxeMovie, "id", scurlMovie.strId); + + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlMovie.ParseElement(pxeLink); + + // calculate the relavance of this hit + std::string sCompareTitle = scurlMovie.strTitle; + StringUtils::ToLower(sCompareTitle); + std::string sMatchTitle = sTitle; + StringUtils::ToLower(sMatchTitle); + + /* + * Identify the best match by performing a fuzzy string compare on the search term and + * the result. Additionally, use the year (if available) to further refine the best match. + * An exact match scores 1, a match off by a year scores 0.5 (release dates can vary between + * countries), otherwise it scores 0. + */ + std::string sCompareYear; + XMLUtils::GetString(pxeMovie, "year", sCompareYear); + + double yearScore = 0; + if (!sYear.empty() && !sCompareYear.empty()) + yearScore = std::max(0.0, 1-0.5*abs(atoi(sYear.c_str())-atoi(sCompareYear.c_str()))); + + scurlMovie.relevance = fstrcmp(sMatchTitle.c_str(), sCompareTitle.c_str(), 0.0) + yearScore; + + // reconstruct a title for the user + if (!sCompareYear.empty()) + scurlMovie.strTitle += StringUtils::Format(" (%s)", sCompareYear.c_str()); + + std::string sLanguage; + if (XMLUtils::GetString(pxeMovie, "language", sLanguage) && !sLanguage.empty()) + scurlMovie.strTitle += StringUtils::Format(" (%s)", sLanguage.c_str()); + + // filter for dupes from naughty scrapers + if (stsDupeCheck.insert(scurlMovie.m_url[0].m_url + " " + scurlMovie.strTitle).second) + vcscurl.push_back(scurlMovie); + } + } + } + + if (!fResults) + throw CScraperError(); // scraper aborted + + if (fSort) + std::stable_sort(vcscurl.begin(), vcscurl.end(), RelevanceSortFunction); + + return vcscurl; +} + +// find album by artist, using fcurl for web fetches +// returns a list of albums (empty if no match or failure) +std::vector<CMusicAlbumInfo> CScraper::FindAlbum(CCurlFile &fcurl, const std::string &sAlbum, + const std::string &sArtist) +{ + CLog::Log(LOGDEBUG, "%s: Searching for '%s - %s' using %s scraper " + "(path: '%s', content: '%s', version: '%s')", __FUNCTION__, sArtist.c_str(), + sAlbum.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + std::vector<CMusicAlbumInfo> vcali; + if (IsNoop()) + return vcali; + + // scraper function is given the album and artist as parameters and + // returns an XML <url> element parseable by CScraperUrl + std::vector<string> extras(2); + g_charsetConverter.utf8To(SearchStringEncoding(), sAlbum, extras[0]); + g_charsetConverter.utf8To(SearchStringEncoding(), sArtist, extras[1]); + extras[0] = CURL::Encode(extras[0]); + extras[1] = CURL::Encode(extras[1]); + CScraperUrl scurl; + vector<string> vcsOut = RunNoThrow("CreateAlbumSearchUrl", scurl, fcurl, &extras); + if (vcsOut.size() > 1) + CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__); + + if (vcsOut.empty() || vcsOut[0].empty()) + return vcali; + scurl.ParseString(vcsOut[0]); + + // the next function is passed the contents of the returned URL, and returns + // an empty string on failure; on success, returns XML matches in the form: + // <results> + // <entity> + // <title>... + // ... (with the usual CScraperUrl decorations like post or spoof) + // ... + // ... + // ... (scale defaults to 1; score is divided by it) + // + // ... + // + vcsOut = RunNoThrow("GetAlbumSearchResults", scurl, fcurl); + + // parse the returned XML into a vector of album objects + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + TiXmlHandle xhDoc(&doc); + + for (TiXmlElement* pxeAlbum = xhDoc.FirstChild("results").FirstChild("entity").Element(); + pxeAlbum; pxeAlbum = pxeAlbum->NextSiblingElement()) + { + std::string sTitle; + if (XMLUtils::GetString(pxeAlbum, "title", sTitle) && !sTitle.empty()) + { + std::string sArtist; + std::string sAlbumName; + if (XMLUtils::GetString(pxeAlbum, "artist", sArtist) && !sArtist.empty()) + sAlbumName = StringUtils::Format("%s - %s", sArtist.c_str(), sTitle.c_str()); + else + sAlbumName = sTitle; + + std::string sYear; + if (XMLUtils::GetString(pxeAlbum, "year", sYear) && !sYear.empty()) + sAlbumName = StringUtils::Format("%s (%s)", sAlbumName.c_str(), sYear.c_str()); + + // if no URL is provided, use the URL we got back from CreateAlbumSearchUrl + // (e.g., in case we only got one result back and were sent to the detail page) + TiXmlElement* pxeLink = pxeAlbum->FirstChildElement("url"); + CScraperUrl scurlAlbum; + if (!pxeLink) + scurlAlbum.ParseString(scurl.m_xml); + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlAlbum.ParseElement(pxeLink); + + if (!scurlAlbum.m_url.size()) + continue; + + CMusicAlbumInfo ali(sTitle, sArtist, sAlbumName, scurlAlbum); + + TiXmlElement* pxeRel = pxeAlbum->FirstChildElement("relevance"); + if (pxeRel && pxeRel->FirstChild()) + { + const char* szScale = pxeRel->Attribute("scale"); + float flScale = szScale ? float(atof(szScale)) : 1; + ali.SetRelevance(float(atof(pxeRel->FirstChild()->Value())) / flScale); + } + + vcali.push_back(ali); + } + } + } + return vcali; +} + +// find artist, using fcurl for web fetches +// returns a list of artists (empty if no match or failure) +std::vector CScraper::FindArtist(CCurlFile &fcurl, + const std::string &sArtist) +{ + CLog::Log(LOGDEBUG, "%s: Searching for '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, sArtist.c_str(), + Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + std::vector vcari; + if (IsNoop()) + return vcari; + + // scraper function is given the artist as parameter and + // returns an XML element parseable by CScraperUrl + std::vector extras(1); + g_charsetConverter.utf8To(SearchStringEncoding(), sArtist, extras[0]); + extras[0] = CURL::Encode(extras[0]); + CScraperUrl scurl; + vector vcsOut = RunNoThrow("CreateArtistSearchUrl", scurl, fcurl, &extras); + + if (vcsOut.empty() || vcsOut[0].empty()) + return vcari; + scurl.ParseString(vcsOut[0]); + + // the next function is passed the contents of the returned URL, and returns + // an empty string on failure; on success, returns XML matches in the form: + // + // + // ... + // ... + // ... + // ... (with the usual CScraperUrl decorations like post or spoof) + // + // ... + // + vcsOut = RunNoThrow("GetArtistSearchResults", scurl, fcurl); + + // parse the returned XML into a vector of artist objects + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + return vcari; + } + TiXmlHandle xhDoc(&doc); + for (TiXmlElement* pxeArtist = xhDoc.FirstChild("results").FirstChild("entity").Element(); + pxeArtist; pxeArtist = pxeArtist->NextSiblingElement()) + { + TiXmlNode* pxnTitle = pxeArtist->FirstChild("title"); + if (pxnTitle && pxnTitle->FirstChild()) + { + CScraperUrl scurlArtist; + + TiXmlElement* pxeLink = pxeArtist->FirstChildElement("url"); + if (!pxeLink) + scurlArtist.ParseString(scurl.m_xml); + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlArtist.ParseElement(pxeLink); + + if (!scurlArtist.m_url.size()) + continue; + + CMusicArtistInfo ari(pxnTitle->FirstChild()->Value(), scurlArtist); + std::string genre; + XMLUtils::GetString(pxeArtist, "genre", genre); + if (!genre.empty()) + ari.GetArtist().genre = StringUtils::Split(genre, g_advancedSettings.m_musicItemSeparator); + XMLUtils::GetString(pxeArtist, "year", ari.GetArtist().strBorn); + + vcari.push_back(ari); + } + } + } + return vcari; +} + +// fetch list of episodes from URL (from video database) +EPISODELIST CScraper::GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl) +{ + EPISODELIST vcep; + if (scurl.m_url.empty()) + return vcep; + + CLog::Log(LOGDEBUG, "%s: Searching '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + scurl.m_url[0].m_url.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + vector vcsIn; + vcsIn.push_back(scurl.m_url[0].m_url); + vector vcsOut = RunNoThrow("GetEpisodeList", scurl, fcurl, &vcsIn); + + // parse the XML response + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML",__FUNCTION__); + continue; + } + + TiXmlHandle xhDoc(&doc); + for (TiXmlElement *pxeMovie = xhDoc.FirstChild("episodeguide").FirstChild("episode"). + Element(); pxeMovie; pxeMovie = pxeMovie->NextSiblingElement()) + { + EPISODE ep; + TiXmlElement *pxeLink = pxeMovie->FirstChildElement("url"); + std::string strEpNum; + if (pxeLink && XMLUtils::GetInt(pxeMovie, "season", ep.iSeason) && + XMLUtils::GetString(pxeMovie, "epnum", strEpNum) && !strEpNum.empty()) + { + CScraperUrl &scurlEp(ep.cScraperUrl); + size_t dot = strEpNum.find("."); + ep.iEpisode = atoi(strEpNum.c_str()); + ep.iSubepisode = (dot != std::string::npos) ? atoi(strEpNum.substr(dot + 1).c_str()) : 0; + if (!XMLUtils::GetString(pxeMovie, "title", scurlEp.strTitle) || scurlEp.strTitle.empty() ) + scurlEp.strTitle = g_localizeStrings.Get(416); + XMLUtils::GetString(pxeMovie, "id", scurlEp.strId); + + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlEp.ParseElement(pxeLink); + + // date must be the format of yyyy-mm-dd + ep.cDate.SetValid(FALSE); + std::string sDate; + if (XMLUtils::GetString(pxeMovie, "aired", sDate) && sDate.length() == 10) + { + tm tm; + if (strptime(sDate.c_str(), "%Y-%m-%d", &tm)) + ep.cDate.SetDate(1900+tm.tm_year, tm.tm_mon + 1, tm.tm_mday); + } + vcep.push_back(ep); + } + } + } + + return vcep; +} + +// takes URL; returns true and populates video details on success, false otherwise +bool CScraper::GetVideoDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + bool fMovie/*else episode*/, CVideoInfoTag &video) +{ + CLog::Log(LOGDEBUG, "%s: Reading %s '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + fMovie ? MediaTypeMovie : MediaTypeEpisode, scurl.m_url[0].m_url.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + video.Reset(); + std::string sFunc = fMovie ? "GetDetails" : "GetEpisodeDetails"; + vector vcsIn; + vcsIn.push_back(scurl.strId); + vcsIn.push_back(scurl.m_url[0].m_url); + vector vcsOut = RunNoThrow(sFunc, scurl, fcurl, &vcsIn); + + // parse XML output + bool fRet(false); + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + continue; + } + + TiXmlHandle xhDoc(&doc); + TiXmlElement *pxeDetails = xhDoc.FirstChild("details").Element(); + if (!pxeDetails) + { + CLog::Log(LOGERROR, "%s: Invalid XML file (want
)", __FUNCTION__); + continue; + } + video.Load(pxeDetails, true/*fChain*/); + fRet = true; // but don't exit in case of chaining + } + return fRet; +} + +// takes a URL; returns true and populates album on success, false otherwise +bool CScraper::GetAlbumDetails(CCurlFile &fcurl, const CScraperUrl &scurl, CAlbum &album) +{ + CLog::Log(LOGDEBUG, "%s: Reading '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + scurl.m_url[0].m_url.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + vector vcsOut = RunNoThrow("GetAlbumDetails", scurl, fcurl); + + // parse the returned XML into an album object (see CAlbum::Load for details) + bool fRet(false); + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + return false; + } + fRet = album.Load(doc.RootElement(), i != vcsOut.begin()); + } + return fRet; +} + +// takes a URL (one returned from FindArtist), the original search string, and +// returns true and populates artist on success, false on failure +bool CScraper::GetArtistDetails(CCurlFile &fcurl, const CScraperUrl &scurl, + const std::string &sSearch, CArtist &artist) +{ + if (!scurl.m_url.size()) + return false; + + CLog::Log(LOGDEBUG, "%s: Reading '%s' ('%s') using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + scurl.m_url[0].m_url.c_str(), sSearch.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + // pass in the original search string for chaining to search other sites + vector vcIn; + vcIn.push_back(sSearch); + vcIn[0] = CURL::Encode(vcIn[0]); + + vector vcsOut = RunNoThrow("GetArtistDetails", scurl, fcurl, &vcIn); + + // ok, now parse the xml file + bool fRet(false); + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + return false; + } + + fRet = artist.Load(doc.RootElement(), i != vcsOut.begin()); + } + return fRet; +} + +} + diff --git a/xbmc/addons/Scraper.h b/xbmc/addons/Scraper.h new file mode 100644 index 0000000..7302972 --- /dev/null +++ b/xbmc/addons/Scraper.h @@ -0,0 +1,178 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "addons/Addon.h" +#include "XBDateTime.h" +#include "utils/ScraperUrl.h" +#include "utils/ScraperParser.h" +#include "video/Episode.h" + +class CAlbum; +class CArtist; +class CVideoInfoTag; + +namespace MUSIC_GRABBER +{ +class CMusicAlbumInfo; +class CMusicArtistInfo; +} + +typedef enum +{ + CONTENT_MOVIES, + CONTENT_TVSHOWS, + CONTENT_MUSICVIDEOS, + CONTENT_ALBUMS, + CONTENT_ARTISTS, + CONTENT_NONE, +} CONTENT_TYPE; + +namespace XFILE +{ + class CCurlFile; +} + +class CScraperUrl; + +namespace ADDON +{ +class CScraper; +typedef std::shared_ptr ScraperPtr; + +std::string TranslateContent(const CONTENT_TYPE &content, bool pretty=false); +CONTENT_TYPE TranslateContent(const std::string &string); +TYPE ScraperTypeFromContent(const CONTENT_TYPE &content); + +// thrown as exception to signal abort or show error dialog +class CScraperError +{ +public: + CScraperError() : m_fAborted(true) {} + CScraperError(const std::string &sTitle, const std::string &sMessage) : + m_fAborted(false), m_sTitle(sTitle), m_sMessage(sMessage) {} + + bool FAborted() const { return m_fAborted; } + const std::string &Title() const { return m_sTitle; } + const std::string &Message() const { return m_sMessage; } + +private: + bool m_fAborted; + std::string m_sTitle; + std::string m_sMessage; +}; + +class CScraper : public CAddon +{ +public: + CScraper(const AddonProps &props) : CAddon(props), m_fLoaded(false) {} + CScraper(const cp_extension_t *ext); + virtual ~CScraper() {} + virtual AddonPtr Clone() const; + + /*! \brief Set the scraper settings for a particular path from an XML string + Loads the default and user settings (if not already loaded) and, if the given XML string is non-empty, + overrides the user settings with the XML. + \param content Content type of the path + \param xml string of XML with the settings. If non-empty this overrides any saved user settings. + \return true if settings are available, false otherwise + \sa GetPathSettings + */ + bool SetPathSettings(CONTENT_TYPE content, const std::string& xml); + + /*! \brief Get the scraper settings for a particular path in the form of an XML string + Loads the default and user settings (if not already loaded) and returns the user settings in the + form or an XML string + \return a string containing the XML settings + \sa SetPathSettings + */ + std::string GetPathSettings(); + + /*! \brief Clear any previously cached results for this scraper + Any previously cached files are cleared if they have been cached for longer than the specified + cachepersistence. + */ + void ClearCache(); + + CONTENT_TYPE Content() const { return m_pathContent; } + const std::string& Language() const { return m_language; } + bool RequiresSettings() const { return m_requiressettings; } + bool Supports(const CONTENT_TYPE &content) const; + + bool IsInUse() const; + bool IsNoop(); + + // scraper media functions + CScraperUrl NfoUrl(const std::string &sNfoContent); + + /*! \brief Resolve an external ID (e.g. MusicBrainz IDs) to a URL using scrapers + If we have an ID in hand, e.g. MusicBrainz IDs or TheTVDB Season IDs + we can get directly to a URL instead of searching by name and choosing from + the search results. The correct scraper type should be used to get the right + URL for a given ID, so we can differentiate albums, artists, TV Seasons, etc. + \param externalID the external ID - e.g. MusicBrainzArtist/AlbumID + \return a populated URL pointing to the details page for the given ID or + an empty URL if we couldn't resolve the ID. + */ + CScraperUrl ResolveIDToUrl(const std::string &externalID); + + std::vector FindMovie(XFILE::CCurlFile &fcurl, + const std::string &sMovie, bool fFirst); + std::vector FindAlbum(XFILE::CCurlFile &fcurl, + const std::string &sAlbum, const std::string &sArtist = ""); + std::vector FindArtist( + XFILE::CCurlFile &fcurl, const std::string &sArtist); + VIDEO::EPISODELIST GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl); + + bool GetVideoDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + bool fMovie/*else episode*/, CVideoInfoTag &video); + bool GetAlbumDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + CAlbum &album); + bool GetArtistDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + const std::string &sSearch, CArtist &artist); + +private: + CScraper(const CScraper &rhs); + std::string SearchStringEncoding() const + { return m_parser.GetSearchStringEncoding(); } + + bool Load(); + std::vector Run(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const std::vector* extras = NULL); + std::vector RunNoThrow(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const std::vector* extras = NULL); + std::string InternalRun(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const std::vector* extras); + + bool m_fLoaded; + std::string m_language; + bool m_requiressettings; + CDateTimeSpan m_persistence; + CONTENT_TYPE m_pathContent; + CScraperParser m_parser; +}; + +} + diff --git a/xbmc/addons/ScreenSaver.cpp b/xbmc/addons/ScreenSaver.cpp new file mode 100644 index 0000000..ef345f5 --- /dev/null +++ b/xbmc/addons/ScreenSaver.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "ScreenSaver.h" +#include "guilib/GraphicContext.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "settings/DisplaySettings.h" +#include "utils/AlarmClock.h" +#include "windowing/WindowingFactory.h" + +// What sound does a python screensaver make? +#define SCRIPT_ALARM "sssssscreensaver" + +#define SCRIPT_TIMEOUT 5 // seconds + +namespace ADDON +{ + + CScreenSaver::CScreenSaver(const char *addonID) + : ADDON::CAddonDll(AddonProps(addonID, ADDON_UNKNOWN, "", "")) + { + } + +AddonPtr CScreenSaver::Clone() const +{ + // Copy constructor is generated by compiler and calls parent copy constructor + return AddonPtr(new CScreenSaver(*this)); +} + +bool CScreenSaver::CreateScreenSaver() +{ + if (CScriptInvocationManager::Get().HasLanguageInvoker(LibPath())) + { + // Don't allow a previously-scheduled alarm to kill our new screensaver + g_alarmClock.Stop(SCRIPT_ALARM, true); + + if (!CScriptInvocationManager::Get().Stop(LibPath())) + CScriptInvocationManager::Get().ExecuteAsync(LibPath(), Clone()); + return true; + } + // pass it the screen width,height + // and the name of the screensaver + int iWidth = g_graphicsContext.GetWidth(); + int iHeight = g_graphicsContext.GetHeight(); + + m_pInfo = new SCR_PROPS; +#ifdef HAS_DX + m_pInfo->device = g_Windowing.Get3DDevice(); +#else + m_pInfo->device = NULL; +#endif + m_pInfo->x = 0; + m_pInfo->y = 0; + m_pInfo->width = iWidth; + m_pInfo->height = iHeight; + m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; + m_pInfo->name = strdup(Name().c_str()); + m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); + m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); + + if (CAddonDll::Create() == ADDON_STATUS_OK) + return true; + + return false; +} + +void CScreenSaver::Start() +{ + // notify screen saver that they should start + if (Initialized()) m_pStruct->Start(); +} + +void CScreenSaver::Render() +{ + // ask screensaver to render itself + if (Initialized()) m_pStruct->Render(); +} + +void CScreenSaver::GetInfo(SCR_INFO *info) +{ + // get info from screensaver + if (Initialized()) m_pStruct->GetInfo(info); +} + +void CScreenSaver::Destroy() +{ +#ifdef HAS_PYTHON + if (URIUtils::HasExtension(LibPath(), ".py")) + { + g_alarmClock.Start(SCRIPT_ALARM, SCRIPT_TIMEOUT, "StopScript(" + LibPath() + ")", true, false); + return; + } +#endif + // Release what was allocated in method CScreenSaver::CreateScreenSaver. + if (m_pInfo) + { + free((void *) m_pInfo->name); + free((void *) m_pInfo->presets); + free((void *) m_pInfo->profile); + + delete m_pInfo; + m_pInfo = NULL; + } + + CAddonDll::Destroy(); +} + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/ScreenSaver.h b/xbmc/addons/ScreenSaver.h new file mode 100644 index 0000000..1b2a741 --- /dev/null +++ b/xbmc/addons/ScreenSaver.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "AddonDll.h" +#include "include/xbmc_scr_types.h" + +typedef DllAddon DllScreenSaver; + +namespace ADDON +{ + +class CScreenSaver : public ADDON::CAddonDll +{ +public: + CScreenSaver(const AddonProps &props) : ADDON::CAddonDll(props) {}; + CScreenSaver(const cp_extension_t *ext) : ADDON::CAddonDll(ext) {}; + CScreenSaver(const char *addonID); + virtual ~CScreenSaver() {} + virtual AddonPtr Clone() const; + + // Things that MUST be supplied by the child classes + bool CreateScreenSaver(); + void Start(); + void Render(); + void GetInfo(SCR_INFO *info); + void Destroy(); +}; + +} /*namespace ADDON*/ diff --git a/xbmc/addons/Service.cpp b/xbmc/addons/Service.cpp new file mode 100644 index 0000000..2fc7670 --- /dev/null +++ b/xbmc/addons/Service.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "Service.h" +#include "AddonManager.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "utils/log.h" +#include "system.h" + +using namespace std; + +namespace ADDON +{ + +CService::CService(const cp_extension_t *ext) + : CAddon(ext), m_type(UNKNOWN), m_startOption(LOGIN) +{ + BuildServiceType(); + + std::string start = CAddonMgr::Get().GetExtValue(ext->configuration, "@start"); + if (start == "startup") + m_startOption = STARTUP; +} + + +CService::CService(const AddonProps &props) + : CAddon(props), m_type(UNKNOWN), m_startOption(LOGIN) +{ + BuildServiceType(); +} + +AddonPtr CService::Clone() const +{ + return AddonPtr(new CService(*this)); +} + +bool CService::Start() +{ + bool ret = true; + switch (m_type) + { +#ifdef HAS_PYTHON + case PYTHON: + ret = (CScriptInvocationManager::Get().ExecuteAsync(LibPath(), this->shared_from_this()) != -1); + break; +#endif + + case UNKNOWN: + default: + ret = false; + break; + } + + return ret; +} + +bool CService::Stop() +{ + bool ret = true; + + switch (m_type) + { +#ifdef HAS_PYTHON + case PYTHON: + ret = CScriptInvocationManager::Get().Stop(LibPath()); + break; +#endif + + case UNKNOWN: + default: + ret = false; + break; + } + + return ret; +} + +void CService::BuildServiceType() +{ + std::string str = LibPath(); + std::string ext; + + size_t p = str.find_last_of('.'); + if (p != string::npos) + ext = str.substr(p + 1); + +#ifdef HAS_PYTHON + std::string pythonExt = ADDON_PYTHON_EXT; + pythonExt.erase(0, 2); + if ( ext == pythonExt ) + m_type = PYTHON; + else +#endif + { + m_type = UNKNOWN; + CLog::Log(LOGERROR, "ADDON: extension '%s' is not currently supported for service addon", ext.c_str()); + } +} + +void CService::OnDisabled() +{ + Stop(); +} + +void CService::OnEnabled() +{ + Start(); +} + +bool CService::OnPreInstall() +{ + // make sure the addon is stopped + AddonPtr localAddon; // need to grab the local addon so we have the correct library path to stop + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_SERVICE, false)) + { + std::shared_ptr service = std::dynamic_pointer_cast(localAddon); + if (service) + service->Stop(); + } + return !CAddonMgr::Get().IsAddonDisabled(ID()); +} + +void CService::OnPostInstall(bool restart, bool update, bool modal) +{ + if (restart) // reload/start it if it was running + { + AddonPtr localAddon; // need to grab the local addon so we have the correct library path to stop + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_SERVICE, false)) + { + std::shared_ptr service = std::dynamic_pointer_cast(localAddon); + if (service) + service->Start(); + } + } +} + +void CService::OnPreUnInstall() +{ + Stop(); +} + +} diff --git a/xbmc/addons/Service.h b/xbmc/addons/Service.h new file mode 100644 index 0000000..f7394de --- /dev/null +++ b/xbmc/addons/Service.h @@ -0,0 +1,63 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "Addon.h" + +namespace ADDON +{ + + class CService: public CAddon + { + public: + + enum TYPE + { + UNKNOWN, + PYTHON + }; + + enum START_OPTION + { + STARTUP, + LOGIN + }; + + CService(const cp_extension_t *ext); + CService(const AddonProps &props); + virtual AddonPtr Clone() const; + + bool Start(); + bool Stop(); + TYPE GetServiceType() { return m_type; } + START_OPTION GetStartOption() { return m_startOption; } + virtual void OnDisabled(); + virtual void OnEnabled(); + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update, bool modal); + virtual void OnPreUnInstall(); + + protected: + void BuildServiceType(); + + private: + TYPE m_type; + START_OPTION m_startOption; + }; +} diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp new file mode 100644 index 0000000..acb5799 --- /dev/null +++ b/xbmc/addons/Skin.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Skin.h" +#include "AddonManager.h" +#include "LangInfo.h" +#include "Util.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/WindowIDs.h" +#include "settings/Settings.h" +#include "settings/lib/Setting.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "ApplicationMessenger.h" + +// fallback for new skin resolution code +#include "filesystem/Directory.h" + +using namespace std; +using namespace XFILE; + +std::shared_ptr g_SkinInfo; + +namespace ADDON +{ + +CSkinInfo::CSkinInfo(const AddonProps &props, const RESOLUTION_INFO &resolution) + : CAddon(props), m_defaultRes(resolution), m_version(""), m_effectsSlowDown(1.f), m_debugging(false) +{ +} + +CSkinInfo::CSkinInfo(const cp_extension_t *ext) + : CAddon(ext), m_version(""), m_effectsSlowDown(1.f) +{ + ELEMENTS elements; + if (CAddonMgr::Get().GetExtElements(ext->configuration, "res", elements)) + { + for (ELEMENTS::iterator i = elements.begin(); i != elements.end(); ++i) + { + int width = atoi(CAddonMgr::Get().GetExtValue(*i, "@width").c_str()); + int height = atoi(CAddonMgr::Get().GetExtValue(*i, "@height").c_str()); + bool defRes = CAddonMgr::Get().GetExtValue(*i, "@default") == "true"; + std::string folder = CAddonMgr::Get().GetExtValue(*i, "@folder"); + float aspect = 0; + std::string strAspect = CAddonMgr::Get().GetExtValue(*i, "@aspect"); + vector 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); + } + } + } + else + { // no resolutions specified -> backward compatibility + std::string defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultwideresolution"); + if (defaultWide.empty()) + defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultresolution"); + TranslateResolution(defaultWide, m_defaultRes); + } + + std::string str = CAddonMgr::Get().GetExtValue(ext->configuration, "@effectslowdown"); + if (!str.empty()) + m_effectsSlowDown = (float)atof(str.c_str()); + + m_debugging = CAddonMgr::Get().GetExtValue(ext->configuration, "@debugging") == "true"; + + LoadStartupWindows(ext); + + // figure out the version + m_version = GetDependencyVersion("xbmc.gui"); +} + +CSkinInfo::~CSkinInfo() +{ +} + +AddonPtr CSkinInfo::Clone() const +{ + return AddonPtr(new CSkinInfo(*this)); +} + +struct closestRes +{ + closestRes(const RESOLUTION_INFO &target) : m_target(target) { }; + bool operator()(const RESOLUTION_INFO &i, const RESOLUTION_INFO &j) + { + float diff = fabs(i.DisplayRatio() - m_target.DisplayRatio()) - fabs(j.DisplayRatio() - m_target.DisplayRatio()); + if (diff < 0) return true; + if (diff > 0) return false; + diff = fabs((float)i.iHeight - m_target.iHeight) - fabs((float)j.iHeight - m_target.iHeight); + if (diff < 0) return true; + if (diff > 0) return false; + return fabs((float)i.iWidth - m_target.iWidth) < fabs((float)j.iWidth - m_target.iWidth); + } + RESOLUTION_INFO m_target; +}; + +void CSkinInfo::Start() +{ + if (!m_resolutions.size()) + { // try falling back to whatever resolutions exist in the directory + CFileItemList items; + CDirectory::GetDirectory(Path(), items, "", DIR_FLAG_NO_FILE_DIRS); + for (int i = 0; i < items.Size(); i++) + { + RESOLUTION_INFO res; + if (items[i]->m_bIsFolder && TranslateResolution(items[i]->GetLabel(), res)) + m_resolutions.push_back(res); + } + } + + if (!m_resolutions.empty()) + { + // find the closest resolution + const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo(); + RESOLUTION_INFO& res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target)); + m_currentAspect = res.strId; + } +} + +std::string CSkinInfo::GetSkinPath(const std::string& strFile, RESOLUTION_INFO *res, const std::string& strBaseDir /* = "" */) const +{ + if (m_resolutions.empty()) + return ""; // invalid skin + + std::string strPathToUse = Path(); + if (!strBaseDir.empty()) + strPathToUse = strBaseDir; + + // if the caller doesn't care about the resolution just use a temporary + RESOLUTION_INFO tempRes; + if (!res) + res = &tempRes; + + // find the closest resolution + const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo(); + *res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target)); + + std::string strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode); + strPath = URIUtils::AddFileToFolder(strPath, strFile); + if (CFile::Exists(strPath)) + return strPath; + + // use the default resolution + *res = m_defaultRes; + + strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode); + strPath = URIUtils::AddFileToFolder(strPath, strFile); + return strPath; +} + +bool CSkinInfo::HasSkinFile(const std::string &strFile) const +{ + return CFile::Exists(GetSkinPath(strFile)); +} + +void CSkinInfo::LoadIncludes() +{ + std::string includesPath = CSpecialProtocol::TranslatePathConvertCase(GetSkinPath("includes.xml")); + CLog::Log(LOGINFO, "Loading skin includes from %s", includesPath.c_str()); + m_includes.ClearIncludes(); + m_includes.LoadIncludes(includesPath); +} + +void CSkinInfo::ResolveIncludes(TiXmlElement *node, std::map* xmlIncludeConditions /* = NULL */) +{ + if(xmlIncludeConditions) + xmlIncludeConditions->clear(); + + m_includes.ResolveIncludes(node, xmlIncludeConditions); +} + +int CSkinInfo::GetStartWindow() const +{ + int windowID = CSettings::Get().GetInt("lookandfeel.startupwindow"); + assert(m_startupWindows.size()); + for (vector::const_iterator it = m_startupWindows.begin(); it != m_startupWindows.end(); ++it) + { + if (windowID == (*it).m_id) + return windowID; + } + // return our first one + return m_startupWindows[0].m_id; +} + +bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext) +{ + m_startupWindows.clear(); + m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513")); + m_startupWindows.push_back(CStartupWindow(WINDOW_TV_CHANNELS, "19180")); + m_startupWindows.push_back(CStartupWindow(WINDOW_RADIO_CHANNELS, "19183")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1")); + m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2")); + m_startupWindows.push_back(CStartupWindow(WINDOW_VIDEOS, "3")); + m_startupWindows.push_back(CStartupWindow(WINDOW_FILES, "7")); + m_startupWindows.push_back(CStartupWindow(WINDOW_SETTINGS_MENU, "5")); + m_startupWindows.push_back(CStartupWindow(WINDOW_WEATHER, "8")); + return true; +} + +void CSkinInfo::GetSkinPaths(std::vector &paths) const +{ + RESOLUTION_INFO res; + GetSkinPath("Home.xml", &res); + if (!res.strMode.empty()) + paths.push_back(URIUtils::AddFileToFolder(Path(), res.strMode)); + if (res.strMode != m_defaultRes.strMode) + paths.push_back(URIUtils::AddFileToFolder(Path(), m_defaultRes.strMode)); +} + +bool CSkinInfo::TranslateResolution(const std::string &name, RESOLUTION_INFO &res) +{ + std::string lower(name); StringUtils::ToLower(lower); + if (lower == "pal") + res = RESOLUTION_INFO(720, 576, 4.0f/3, "pal"); + else if (lower == "pal16x9") + res = RESOLUTION_INFO(720, 576, 16.0f/9, "pal16x9"); + else if (lower == "ntsc") + res = RESOLUTION_INFO(720, 480, 4.0f/3, "ntsc"); + else if (lower == "ntsc16x9") + res = RESOLUTION_INFO(720, 480, 16.0f/9, "ntsc16x9"); + else if (lower == "720p") + res = RESOLUTION_INFO(1280, 720, 0, "720p"); + else if (lower == "1080i") + res = RESOLUTION_INFO(1920, 1080, 0, "1080i"); + else + return false; + return true; +} + +int CSkinInfo::GetFirstWindow() const +{ + int startWindow = GetStartWindow(); + if (HasSkinFile("Startup.xml")) + startWindow = WINDOW_STARTUP_ANIM; + return startWindow; +} + +bool CSkinInfo::IsInUse() const +{ + // Could extend this to prompt for reverting to the standard skin perhaps + return CSettings::Get().GetString("lookandfeel.skin") == ID(); +} + +const INFO::CSkinVariableString* CSkinInfo::CreateSkinVariable(const std::string& name, int context) +{ + return m_includes.CreateSkinVariable(name, context); +} + +bool CSkinInfo::OnPreInstall() +{ + // check whether this is an active skin - we need to unload it if so + if (IsInUse()) + { + CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true); + return true; + } + return false; +} + +void CSkinInfo::OnPostInstall(bool restart, bool update, bool modal) +{ + if (restart || (!update && !modal && CGUIDialogYesNo::ShowAndGetInput(Name(), g_localizeStrings.Get(24099),"",""))) + { + CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); + if (toast) + { + toast->ResetTimer(); + toast->Close(true); + } + if (CSettings::Get().GetString("lookandfeel.skin") == ID()) + CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin", true); + else + CSettings::Get().SetString("lookandfeel.skin", ID()); + } +} + +void CSkinInfo::SettingOptionsSkinColorsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + // Remove the .xml extension from the Themes + if (URIUtils::HasExtension(settingValue, ".xml")) + URIUtils::RemoveExtension(settingValue); + current = "SKINDEFAULT"; + + // There is a default theme (just defaults.xml) + // any other *.xml files are additional color themes on top of this one. + + // add the default label + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); // the standard defaults.xml will be used! + + // Search for colors in the Current skin! + vector vecColors; + string strPath = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "colors"); + + CFileItemList items; + CDirectory::GetDirectory(CSpecialProtocol::TranslatePathConvertCase(strPath), items, ".xml"); + // Search for Themes in the Current skin! + for (int i = 0; i < items.Size(); ++i) + { + CFileItemPtr pItem = items[i]; + if (!pItem->m_bIsFolder && !StringUtils::EqualsNoCase(pItem->GetLabel(), "defaults.xml")) + { // not the default one + vecColors.push_back(pItem->GetLabel().substr(0, pItem->GetLabel().size() - 4)); + } + } + sort(vecColors.begin(), vecColors.end(), sortstringbyname()); + for (int i = 0; i < (int) vecColors.size(); ++i) + list.push_back(make_pair(vecColors[i], vecColors[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsSkinFontsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + bool currentValueSet = false; + std::string strPath = g_SkinInfo->GetSkinPath("Font.xml"); + + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(strPath)) + { + CLog::Log(LOGERROR, "FillInSkinFonts: Couldn't load %s", strPath.c_str()); + return; + } + + const TiXmlElement* pRootElement = xmlDoc.RootElement(); + if (!pRootElement || pRootElement->ValueStr() != "fonts") + { + CLog::Log(LOGERROR, "FillInSkinFonts: file %s doesn't start with ", strPath.c_str()); + return; + } + + const TiXmlElement *pChild = pRootElement->FirstChildElement("fontset"); + while (pChild) + { + const char* idAttr = pChild->Attribute("id"); + const char* idLocAttr = pChild->Attribute("idloc"); + if (idAttr != NULL) + { + if (idLocAttr) + list.push_back(make_pair(g_localizeStrings.Get(atoi(idLocAttr)), idAttr)); + else + list.push_back(make_pair(idAttr, idAttr)); + + if (StringUtils::EqualsNoCase(idAttr, settingValue)) + currentValueSet = true; + } + pChild = pChild->NextSiblingElement("fontset"); + } + + if (list.empty()) + { // Since no fontset is defined, there is no selection of a fontset, so disable the component + list.push_back(make_pair(g_localizeStrings.Get(13278), "")); + current = ""; + currentValueSet = true; + } + + if (!currentValueSet) + current = list[0].second; +} + +void CSkinInfo::SettingOptionsSkinSoundFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + current = "SKINDEFAULT"; + + //find skins... + CFileItemList items; + CDirectory::GetDirectory("special://xbmc/sounds/", items); + CDirectory::GetDirectory("special://home/sounds/", items); + + vector vecSoundSkins; + for (int i = 0; i < items.Size(); i++) + { + CFileItemPtr pItem = items[i]; + if (pItem->m_bIsFolder) + { + if (StringUtils::EqualsNoCase(pItem->GetLabel(), ".svn") || + StringUtils::EqualsNoCase(pItem->GetLabel(), "fonts") || + StringUtils::EqualsNoCase(pItem->GetLabel(), "media")) + continue; + + vecSoundSkins.push_back(pItem->GetLabel()); + } + } + + list.push_back(make_pair(g_localizeStrings.Get(474), "OFF")); + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); + + sort(vecSoundSkins.begin(), vecSoundSkins.end(), sortstringbyname()); + for (unsigned int i = 0; i < vecSoundSkins.size(); i++) + list.push_back(make_pair(vecSoundSkins[i], vecSoundSkins[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsSkinThemesFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + // get the choosen theme and remove the extension from the current theme (backward compat) + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + URIUtils::RemoveExtension(settingValue); + current = "SKINDEFAULT"; + + // there is a default theme (just Textures.xpr/xbt) + // any other *.xpr|*.xbt files are additional themes on top of this one. + + // add the default Label + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); // the standard Textures.xpr/xbt will be used + + // search for themes in the current skin! + vector vecTheme; + CUtil::GetSkinThemes(vecTheme); + + // sort the themes for GUI and list them + for (int i = 0; i < (int) vecTheme.size(); ++i) + list.push_back(make_pair(vecTheme[i], vecTheme[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsStartupWindowsFiller(const CSetting *setting, std::vector< std::pair > &list, int ¤t, void *data) +{ + int settingValue = ((const CSettingInt *)setting)->GetValue(); + current = -1; + + const vector &startupWindows = g_SkinInfo->GetStartupWindows(); + + for (vector::const_iterator it = startupWindows.begin(); it != startupWindows.end(); ++it) + { + string windowName = it->m_name; + if (StringUtils::IsNaturalNumber(windowName)) + windowName = g_localizeStrings.Get(atoi(windowName.c_str())); + int windowID = it->m_id; + + list.push_back(make_pair(windowName, windowID)); + + if (settingValue == windowID) + current = settingValue; + } + + // if the current value hasn't been properly set, set it to the first window in the list + if (current < 0) + current = list[0].second; +} + +} /*namespace ADDON*/ diff --git a/xbmc/addons/Skin.h b/xbmc/addons/Skin.h new file mode 100644 index 0000000..42bddf8 --- /dev/null +++ b/xbmc/addons/Skin.h @@ -0,0 +1,155 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include + +#include "Addon.h" +#include "guilib/GraphicContext.h" // needed for the RESOLUTION members +#include "guilib/GUIIncludes.h" // needed for the GUIInclude member +#define CREDIT_LINE_LENGTH 50 + +class CSetting; + +namespace ADDON +{ + +class CSkinInfo : public CAddon +{ +public: + class CStartupWindow + { + public: + CStartupWindow(int id, const std::string &name): + m_id(id), m_name(name) + { + }; + int m_id; + std::string m_name; + }; + + //FIXME remove this, kept for current repo handling + CSkinInfo(const AddonProps &props, const RESOLUTION_INFO &res = RESOLUTION_INFO()); + CSkinInfo(const cp_extension_t *ext); + virtual ~CSkinInfo(); + virtual AddonPtr Clone() const; + + /*! \brief Load resultion information from directories in Path(). + */ + void Start(); + + bool HasSkinFile(const std::string &strFile) const; + + /*! \brief Get the full path to the specified file in the skin + We search for XML files in the skin folder that best matches the current resolution. + \param file XML file to look for + \param res [out] If non-NULL, the resolution that the returned XML file is in is returned. Defaults to NULL. + \param baseDir [in] If non-empty, the given directory is searched instead of the skin's directory. Defaults to empty. + \return path to the XML file + */ + std::string GetSkinPath(const std::string& file, RESOLUTION_INFO *res = NULL, const std::string& baseDir = "") const; + + AddonVersion APIVersion() const { return m_version; }; + + /*! \brief Return whether skin debugging is enabled + \return true if skin debugging (set via true in skin.xml) is enabled. + */ + bool IsDebugging() const { return m_debugging; }; + + /*! \brief Get the id of the first window to load + The first window is generally Startup.xml unless it doesn't exist or if the skinner + has specified which start windows they support and the user is going to somewhere other + than the home screen. + \return id of the first window to load + */ + int GetFirstWindow() const; + + /*! \brief Get the id of the window the user wants to start in after any skin animation + \return id of the start window + */ + int GetStartWindow() const; + + /*! \brief Translate a resolution string + \param name the string to translate + \param res [out] the resolution structure if name is valid + \return true if the resolution is valid, false otherwise + */ + static bool TranslateResolution(const std::string &name, RESOLUTION_INFO &res); + + void ResolveIncludes(TiXmlElement *node, std::map* xmlIncludeConditions = NULL); + + float GetEffectsSlowdown() const { return m_effectsSlowDown; }; + + const std::vector &GetStartupWindows() const { return m_startupWindows; }; + + /*! \brief Retrieve the skin paths to search for skin XML files + \param paths [out] vector of paths to search, in order. + */ + void GetSkinPaths(std::vector &paths) const; + + bool IsInUse() const; + + const std::string& GetCurrentAspect() const { return m_currentAspect; } + + void LoadIncludes(); + const INFO::CSkinVariableString* CreateSkinVariable(const std::string& name, int context); + + static void SettingOptionsSkinColorsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsSkinFontsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsSkinSoundFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsSkinThemesFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsStartupWindowsFiller(const CSetting *setting, std::vector< std::pair > &list, int ¤t, void *data); + + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update, bool modal); +protected: + /*! \brief Given a resolution, retrieve the corresponding directory name + \param res RESOLUTION to translate + \return directory name for res + */ + std::string GetDirFromRes(RESOLUTION res) const; + + /*! \brief grab a resolution tag from a skin's configuration data + \param props passed addoninfo structure to check for resolution + \param tag name of the tag to look for + \param res resolution to return + \return true if we find a valid resolution, false otherwise + */ + void GetDefaultResolution(const cp_extension_t *ext, const char *tag, RESOLUTION &res, const RESOLUTION &def) const; + + bool LoadStartupWindows(const cp_extension_t *ext); + + RESOLUTION_INFO m_defaultRes; + std::vector m_resolutions; + + AddonVersion m_version; + + float m_effectsSlowDown; + CGUIIncludes m_includes; + std::string m_currentAspect; + + std::vector m_startupWindows; + bool m_debugging; +}; + +} /*namespace ADDON*/ + +extern std::shared_ptr g_SkinInfo; diff --git a/xbmc/addons/Visualisation.cpp b/xbmc/addons/Visualisation.cpp new file mode 100644 index 0000000..a64ee59 --- /dev/null +++ b/xbmc/addons/Visualisation.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "system.h" +#include "Visualisation.h" +#include "utils/fft.h" +#include "GUIInfoManager.h" +#include "Application.h" +#include "guilib/GraphicContext.h" +#include "guilib/WindowIDs.h" +#include "music/tags/MusicInfoTag.h" +#include "settings/Settings.h" +#include "settings/AdvancedSettings.h" +#include "settings/DisplaySettings.h" +#include "windowing/WindowingFactory.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "cores/IPlayer.h" +#include "cores/AudioEngine/AEFactory.h" +#ifdef TARGET_POSIX +#include +#include "filesystem/SpecialProtocol.h" +#endif + +using namespace std; +using namespace MUSIC_INFO; +using namespace ADDON; + +CAudioBuffer::CAudioBuffer(int iSize) +{ + m_iLen = iSize; + m_pBuffer = new float[iSize]; +} + +CAudioBuffer::~CAudioBuffer() +{ + delete [] m_pBuffer; +} + +const float* CAudioBuffer::Get() const +{ + return m_pBuffer; +} + +void CAudioBuffer::Set(const float* psBuffer, int iSize) +{ + if (iSize<0) + return; + memcpy(m_pBuffer, psBuffer, iSize * sizeof(float)); + for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0; +} + +bool CVisualisation::Create(int x, int y, int w, int h, void *device) +{ + m_pInfo = new VIS_PROPS; + #ifdef HAS_DX + m_pInfo->device = g_Windowing.Get3DDevice(); +#else + m_pInfo->device = NULL; +#endif + m_pInfo->x = x; + m_pInfo->y = y; + m_pInfo->width = w; + m_pInfo->height = h; + m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; + + m_pInfo->name = strdup(Name().c_str()); + m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); + m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); + m_pInfo->submodule = NULL; + + if (CAddonDll::Create() == ADDON_STATUS_OK) + { + // Start the visualisation + std::string strFile = URIUtils::GetFileName(g_application.CurrentFile()); + CLog::Log(LOGDEBUG, "Visualisation::Start()\n"); + try + { + m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile.c_str()); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Start() (CVisualisation::Create)"); + return false; + } + + GetPresets(); + + if (GetSubModules()) + m_pInfo->submodule = strdup(CSpecialProtocol::TranslatePath(m_submodules.front()).c_str()); + else + m_pInfo->submodule = NULL; + + CreateBuffers(); + + CAEFactory::RegisterAudioCallback(this); + + return true; + } + return false; +} + +void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName) +{ + // notify visz. that new song has been started + // pass it the nr of audio channels, sample rate, bits/sample and offcourse the songname + if (Initialized()) + { + try + { + m_pStruct->Start(iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str()); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Start (CVisualisation::Start)"); + } + } +} + +void CVisualisation::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength) +{ + // pass audio data to visz. + // audio data: is short audiodata [channel][iAudioDataLength] containing the raw audio data + // iAudioDataLength = length of audiodata array + // pFreqData = fft-ed audio data + // iFreqDataLength = length of pFreqData + if (Initialized()) + { + try + { + m_pStruct->AudioData(pAudioData, iAudioDataLength, pFreqData, iFreqDataLength); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->AudioData (CVisualisation::AudioData)"); + } + } +} + +void CVisualisation::Render() +{ + // ask visz. to render itself + g_graphicsContext.BeginPaint(); + if (Initialized()) + { + try + { + m_pStruct->Render(); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Render (CVisualisation::Render)"); + } + } + g_graphicsContext.EndPaint(); +} + +void CVisualisation::Stop() +{ + CAEFactory::UnregisterAudioCallback(); + if (Initialized()) + { + CAddonDll::Stop(); + } +} + +void CVisualisation::GetInfo(VIS_INFO *info) +{ + if (Initialized()) + { + try + { + m_pStruct->GetInfo(info); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->GetInfo (CVisualisation::GetInfo)"); + } + } +} + +bool CVisualisation::OnAction(VIS_ACTION action, void *param) +{ + if (!Initialized()) + return false; + + // see if vis wants to handle the input + // returns false if vis doesnt want the input + // returns true if vis handled the input + try + { + if (action != VIS_ACTION_NONE && m_pStruct->OnAction) + { + // if this is a VIS_ACTION_UPDATE_TRACK action, copy relevant + // tags from CMusicInfoTag to VisTag + if ( action == VIS_ACTION_UPDATE_TRACK && param ) + { + const CMusicInfoTag* tag = (const CMusicInfoTag*)param; + std::string artist(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)); + std::string albumArtist(StringUtils::Join(tag->GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator)); + std::string genre(StringUtils::Join(tag->GetGenre(), g_advancedSettings.m_musicItemSeparator)); + + VisTrack track; + track.title = tag->GetTitle().c_str(); + track.artist = artist.c_str(); + track.album = tag->GetAlbum().c_str(); + track.albumArtist = albumArtist.c_str(); + track.genre = genre.c_str(); + track.comment = tag->GetComment().c_str(); + track.lyrics = tag->GetLyrics().c_str(); + track.trackNumber = tag->GetTrackNumber(); + track.discNumber = tag->GetDiscNumber(); + track.duration = tag->GetDuration(); + track.year = tag->GetYear(); + track.rating = tag->GetRating(); + + return m_pStruct->OnAction(action, &track); + } + return m_pStruct->OnAction((int)action, param); + } + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->OnAction (CVisualisation::OnAction)"); + } + return false; +} + +void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample) +{ + if (!m_pStruct) + return ; + CLog::Log(LOGDEBUG, "OnInitialize() started"); + + m_iChannels = iChannels; + m_iSamplesPerSec = iSamplesPerSec; + m_iBitsPerSample = iBitsPerSample; + UpdateTrack(); + + CLog::Log(LOGDEBUG, "OnInitialize() done"); +} + +void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength) +{ + if (!m_pStruct) + return ; + + // FIXME: iAudioDataLength should never be less than 0 + if (iAudioDataLength<0) + return; + + // Save our audio data in the buffers + unique_ptr pBuffer ( new CAudioBuffer(AUDIO_BUFFER_SIZE) ); + pBuffer->Set(pAudioData, iAudioDataLength); + m_vecBuffers.push_back( pBuffer.release() ); + + if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ; + + unique_ptr ptrAudioBuffer ( m_vecBuffers.front() ); + m_vecBuffers.pop_front(); + // Fourier transform the data if the vis wants it... + if (m_bWantsFreq) + { + const float *psAudioData = ptrAudioBuffer->Get(); + memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float)); + + // FFT the data + twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE); + + // Normalize the data + float fMinData = (float)AUDIO_BUFFER_SIZE * AUDIO_BUFFER_SIZE * 3 / 8 * 0.5 * 0.5; // 3/8 for the Hann window, 0.5 as minimum amplitude + float fInvMinData = 1.0f/fMinData; + for (int i = 0; i < AUDIO_BUFFER_SIZE + 2; i++) + { + m_fFreq[i] *= fInvMinData; + } + + // Transfer data to our visualisation + AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE); + } + else + { // Transfer data to our visualisation + AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0); + } + return ; +} + +void CVisualisation::CreateBuffers() +{ + ClearBuffers(); + + // Get the number of buffers from the current vis + VIS_INFO info; + m_pStruct->GetInfo(&info); + m_iNumBuffers = info.iSyncDelay + 1; + m_bWantsFreq = (info.bWantsFreq != 0); + if (m_iNumBuffers > MAX_AUDIO_BUFFERS) + m_iNumBuffers = MAX_AUDIO_BUFFERS; + if (m_iNumBuffers < 1) + m_iNumBuffers = 1; +} + +void CVisualisation::ClearBuffers() +{ + m_bWantsFreq = false; + m_iNumBuffers = 0; + + while (!m_vecBuffers.empty()) + { + CAudioBuffer* pAudioBuffer = m_vecBuffers.front(); + delete pAudioBuffer; + m_vecBuffers.pop_front(); + } + for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++) + { + m_fFreq[j] = 0.0f; + } +} + +bool CVisualisation::UpdateTrack() +{ + bool handled = false; + if (Initialized()) + { + // get the current album art filename + m_AlbumThumb = CSpecialProtocol::TranslatePath(g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID)); + + // get the current track tag + const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag(); + + if (m_AlbumThumb == "DefaultAlbumCover.png") + m_AlbumThumb = ""; + else + CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str()); + + // inform the visualisation of the current album art + if (OnAction( VIS_ACTION_UPDATE_ALBUMART, (void*)( m_AlbumThumb.c_str() ) ) ) + handled = true; + + // inform the visualisation of the current track's tag information + if ( tag && OnAction( VIS_ACTION_UPDATE_TRACK, (void*)tag ) ) + handled = true; + } + return handled; +} + +bool CVisualisation::GetPresetList(std::vector &vecpresets) +{ + vecpresets = m_presets; + return !m_presets.empty(); +} + +bool CVisualisation::GetPresets() +{ + m_presets.clear(); + char **presets = NULL; + unsigned int entries = 0; + try + { + entries = m_pStruct->GetPresets(&presets); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->OnAction (CVisualisation::GetPresets)"); + return false; + } + if (presets && entries > 0) + { + for (unsigned i=0; i < entries; i++) + { + if (presets[i]) + { + m_presets.push_back(presets[i]); + } + } + } + return (!m_presets.empty()); +} + +bool CVisualisation::GetSubModuleList(std::vector &vecmodules) +{ + vecmodules = m_submodules; + return !m_submodules.empty(); +} + +bool CVisualisation::GetSubModules() +{ + m_submodules.clear(); + char **modules = NULL; + unsigned int entries = 0; + try + { + entries = m_pStruct->GetSubModules(&modules); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in Visualisation::GetSubModules()"); + return false; + } + if (modules && entries > 0) + { + for (unsigned i=0; i < entries; i++) + { + if (modules[i]) + { + m_submodules.push_back(modules[i]); + } + } + } + return (!m_submodules.empty()); +} + +std::string CVisualisation::GetFriendlyName(const std::string& strVisz, + const std::string& strSubModule) +{ + // should be of the format "moduleName (visName)" + return strSubModule + " (" + strVisz + ")"; +} + +bool CVisualisation::IsLocked() +{ + if (!m_presets.empty()) + { + if (!m_pStruct) + return false; + + return m_pStruct->IsLocked(); + } + return false; +} + +void CVisualisation::Destroy() +{ + // Free what was allocated in method CVisualisation::Create + if (m_pInfo) + { + free((void *) m_pInfo->name); + free((void *) m_pInfo->presets); + free((void *) m_pInfo->profile); + free((void *) m_pInfo->submodule); + + delete m_pInfo; + m_pInfo = NULL; + } + + CAddonDll::Destroy(); +} + +unsigned CVisualisation::GetPreset() +{ + unsigned index = 0; + try + { + index = m_pStruct->GetPreset(); + } + catch(...) + { + return 0; + } + return index; +} + +std::string CVisualisation::GetPresetName() +{ + if (!m_presets.empty()) + return m_presets[GetPreset()]; + else + return ""; +} + diff --git a/xbmc/addons/Visualisation.h b/xbmc/addons/Visualisation.h new file mode 100644 index 0000000..0a2a1cb --- /dev/null +++ b/xbmc/addons/Visualisation.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#pragma once + +#include "AddonDll.h" +#include "cores/IAudioCallback.h" +#include "include/xbmc_vis_types.h" +#include "guilib/IRenderingCallback.h" + +#include +#include +#include + +#define AUDIO_BUFFER_SIZE 512 // MUST BE A POWER OF 2!!! +#define MAX_AUDIO_BUFFERS 16 + +class CCriticalSection; + +typedef DllAddon DllVisualisation; + +class CAudioBuffer +{ +public: + CAudioBuffer(int iSize); + virtual ~CAudioBuffer(); + const float* Get() const; + void Set(const float* psBuffer, int iSize); +private: + CAudioBuffer(); + float* m_pBuffer; + int m_iLen; +}; + +namespace ADDON +{ + class CVisualisation : public CAddonDll + , public IAudioCallback + , public IRenderingCallback + { + public: + CVisualisation(const ADDON::AddonProps &props) : CAddonDll(props) {} + CVisualisation(const cp_extension_t *ext) : CAddonDll(ext) {} + virtual void OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample); + virtual void OnAudioData(const float* pAudioData, int iAudioDataLength); + bool Create(int x, int y, int w, int h, void *device); + void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName); + void AudioData(const float *pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); + void Render(); + void Stop(); + void GetInfo(VIS_INFO *info); + bool OnAction(VIS_ACTION action, void *param = NULL); + bool UpdateTrack(); + bool HasSubModules() { return !m_submodules.empty(); } + bool IsLocked(); + unsigned GetPreset(); + std::string GetPresetName(); + bool GetPresetList(std::vector& vecpresets); + bool GetSubModuleList(std::vector& vecmodules); + static std::string GetFriendlyName(const std::string& vis, const std::string& module); + void Destroy(); + + private: + void CreateBuffers(); + void ClearBuffers(); + + bool GetPresets(); + bool GetSubModules(); + + // attributes of the viewport we render to + int m_xPos; + int m_yPos; + int m_width; + int m_height; + + // cached preset list + std::vector m_presets; + // cached submodule list + std::vector m_submodules; + int m_currentModule; + + // audio properties + int m_iChannels; + int m_iSamplesPerSec; + int m_iBitsPerSample; + std::list m_vecBuffers; + int m_iNumBuffers; // Number of Audio buffers + bool m_bWantsFreq; + float m_fFreq[2*AUDIO_BUFFER_SIZE]; // Frequency data + bool m_bCalculate_Freq; // True if the vis wants freq data + + // track information + std::string m_AlbumThumb; + }; +} diff --git a/xbmc/addons/Webinterface.cpp b/xbmc/addons/Webinterface.cpp new file mode 100644 index 0000000..5745b1c --- /dev/null +++ b/xbmc/addons/Webinterface.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Webinterface.h" +#include "addons/AddonManager.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" + +using namespace ADDON; + +CWebinterface::CWebinterface(const ADDON::AddonProps &props, WebinterfaceType type /* = WebinterfaceTypeStatic */, const std::string &entryPoint /* = "WEBINTERFACE_DEFAULT_ENTRY_POINT" */) + : CAddon(props), + m_type(type), + m_entryPoint(entryPoint) +{ } + +CWebinterface::CWebinterface(const cp_extension_t *ext) + : CAddon(ext), + m_type(WebinterfaceTypeStatic), + m_entryPoint(WEBINTERFACE_DEFAULT_ENTRY_POINT) +{ + // determine the type of the webinterface + std::string webinterfaceType = CAddonMgr::Get().GetExtValue(ext->configuration, "@type"); + if (StringUtils::EqualsNoCase(webinterfaceType.c_str(), "wsgi")) + m_type = WebinterfaceTypeWsgi; + else if (!webinterfaceType.empty() && !StringUtils::EqualsNoCase(webinterfaceType.c_str(), "static") && !StringUtils::EqualsNoCase(webinterfaceType.c_str(), "html")) + CLog::Log(LOGWARNING, "Webinterface addon \"%s\" has specified an unsupported type \"%s\"", ID().c_str(), webinterfaceType.c_str()); + + // determine the entry point of the webinterface + std::string entryPoint = CAddonMgr::Get().GetExtValue(ext->configuration, "@entry"); + if (!entryPoint.empty()) + m_entryPoint = entryPoint; +} + +CWebinterface::~CWebinterface() +{ } + +std::string CWebinterface::GetEntryPoint(const std::string &path) const +{ + if (m_type == WebinterfaceTypeWsgi) + return LibPath(); + + return URIUtils::AddFileToFolder(path, m_entryPoint); +} + +std::string CWebinterface::GetBaseLocation() const +{ + if (m_type == WebinterfaceTypeWsgi) + return "/addons/" + ID(); + + return ""; +} + +AddonPtr CWebinterface::Clone() const +{ + return AddonPtr(new CWebinterface(*this)); +} \ No newline at end of file diff --git a/xbmc/addons/Webinterface.h b/xbmc/addons/Webinterface.h new file mode 100644 index 0000000..0a4773f --- /dev/null +++ b/xbmc/addons/Webinterface.h @@ -0,0 +1,54 @@ +#pragma once +/* + * Copyright (C) 2015 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Addon.h" + +#define WEBINTERFACE_DEFAULT_ENTRY_POINT "index.html" + +namespace ADDON +{ + typedef enum WebinterfaceType + { + WebinterfaceTypeStatic = 0, + WebinterfaceTypeWsgi + } WebinterfaceType; + + class CWebinterface : public CAddon + { + public: + explicit CWebinterface(const ADDON::AddonProps &props, WebinterfaceType type = WebinterfaceTypeStatic, const std::string &entryPoint = WEBINTERFACE_DEFAULT_ENTRY_POINT); + explicit CWebinterface(const cp_extension_t *ext); + virtual ~CWebinterface(); + + WebinterfaceType GetType() const { return m_type; } + const std::string& EntryPoint() const { return m_entryPoint; } + + std::string GetEntryPoint(const std::string &path) const; + std::string GetBaseLocation() const; + + // specializations of CAddon + virtual AddonPtr Clone() const; + + private: + WebinterfaceType m_type; + std::string m_entryPoint; + }; +} diff --git a/xbmc/addons/addon-bindings.mk b/xbmc/addons/addon-bindings.mk new file mode 100644 index 0000000..50d75bc --- /dev/null +++ b/xbmc/addons/addon-bindings.mk @@ -0,0 +1,20 @@ +BINDINGS =xbmc/addons/include/xbmc_addon_cpp_dll.h +BINDINGS+=xbmc/addons/include/xbmc_addon_dll.h +BINDINGS+=xbmc/addons/include/xbmc_addon_types.h +BINDINGS+=xbmc/addons/include/xbmc_audioenc_dll.h +BINDINGS+=xbmc/addons/include/xbmc_audioenc_types.h +BINDINGS+=xbmc/addons/include/xbmc_codec_types.h +BINDINGS+=xbmc/addons/include/xbmc_epg_types.h +BINDINGS+=xbmc/addons/include/xbmc_pvr_dll.h +BINDINGS+=xbmc/addons/include/xbmc_pvr_types.h +BINDINGS+=xbmc/addons/include/xbmc_scr_dll.h +BINDINGS+=xbmc/addons/include/xbmc_scr_types.h +BINDINGS+=xbmc/addons/include/xbmc_vis_dll.h +BINDINGS+=xbmc/addons/include/xbmc_vis_types.h +BINDINGS+=xbmc/addons/include/xbmc_stream_utils.hpp +BINDINGS+=addons/library.xbmc.addon/libXBMC_addon.h +BINDINGS+=addons/library.xbmc.gui/libXBMC_gui.h +BINDINGS+=addons/library.xbmc.pvr/libXBMC_pvr.h +BINDINGS+=addons/library.xbmc.codec/libXBMC_codec.h +BINDINGS+=xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h + diff --git a/xbmc/addons/include/NOTE b/xbmc/addons/include/NOTE new file mode 100644 index 0000000..dbcc329 --- /dev/null +++ b/xbmc/addons/include/NOTE @@ -0,0 +1,12 @@ +NOTE: + +This directory contains independent Headers to build Add-on's +without the whole XBMC source tree. The Add-on itself can add +this headers to his source tree without dependencies to any +XBMC related classes or functions. + +Also this headers are never changed without a API Version +change. + +The current PVR API version can be found in xbmc_pvr_types.h: +XBMC_PVR_API_VERSION diff --git a/xbmc/addons/include/xbmc_addon_cpp_dll.h b/xbmc/addons/include/xbmc_addon_cpp_dll.h new file mode 100644 index 0000000..3944525 --- /dev/null +++ b/xbmc/addons/include/xbmc_addon_cpp_dll.h @@ -0,0 +1,191 @@ +#ifndef __XBMC_ADDON_CPP_H__ +#define __XBMC_ADDON_CPP_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "xbmc_addon_types.h" + +#include +#include +#include + +class DllSetting +{ +public: + enum SETTING_TYPE { NONE=0, CHECK, SPIN }; + + DllSetting(SETTING_TYPE t, const char *n, const char *l) + { + id = NULL; + label = NULL; + if (n) + { + id = new char[strlen(n)+1]; + strcpy(id, n); + } + if (l) + { + label = new char[strlen(l)+1]; + strcpy(label, l); + } + current = 0; + type = t; + } + + DllSetting(const DllSetting &rhs) // copy constructor + { + id = NULL; + label = NULL; + if (rhs.id) + { + id = new char[strlen(rhs.id)+1]; + strcpy(id, rhs.id); + } + if (rhs.label) + { + label = new char[strlen(rhs.label)+1]; + strcpy(label, rhs.label); + } + current = rhs.current; + type = rhs.type; + for (unsigned int i = 0; i < rhs.entry.size(); i++) + { + char *lab = new char[strlen(rhs.entry[i]) + 1]; + strcpy(lab, rhs.entry[i]); + entry.push_back(lab); + } + } + + ~DllSetting() + { + delete[] id; + delete[] label; + for (unsigned int i=0; i < entry.size(); i++) + delete[] entry[i]; + } + + void AddEntry(const char *label) + { + if (!label || type != SPIN) return; + char *lab = new char[strlen(label) + 1]; + strcpy(lab, label); + entry.push_back(lab); + } + + // data members + SETTING_TYPE type; + char* id; + char* label; + int current; + std::vector entry; +}; + +class DllUtils +{ +public: + + static unsigned int VecToStruct(std::vector &vecSet, ADDON_StructSetting*** sSet) + { + *sSet = NULL; + if(vecSet.size() == 0) + return 0; + + unsigned int uiElements=0; + + *sSet = (ADDON_StructSetting**)malloc(vecSet.size()*sizeof(ADDON_StructSetting*)); + for(unsigned int i=0;iid = NULL; + (*sSet)[i]->label = NULL; + uiElements++; + + if (vecSet[i].id && vecSet[i].label) + { + (*sSet)[i]->id = strdup(vecSet[i].id); + (*sSet)[i]->label = strdup(vecSet[i].label); + (*sSet)[i]->type = vecSet[i].type; + (*sSet)[i]->current = vecSet[i].current; + (*sSet)[i]->entry_elements = 0; + (*sSet)[i]->entry = NULL; + if(vecSet[i].type == DllSetting::SPIN && vecSet[i].entry.size() > 0) + { + (*sSet)[i]->entry = (char**)malloc(vecSet[i].entry.size()*sizeof(char**)); + for(unsigned int j=0;j 0) + { + (*sSet)[i]->entry[j] = strdup(vecSet[i].entry[j]); + (*sSet)[i]->entry_elements++; + } + } + } + } + } + return uiElements; + } + + static void StructToVec(unsigned int iElements, ADDON_StructSetting*** sSet, std::vector *vecSet) + { + if(iElements == 0) + return; + + vecSet->clear(); + for(unsigned int i=0;itype, (*sSet)[i]->id, (*sSet)[i]->label); + if((*sSet)[i]->type == DllSetting::SPIN) + { + for(unsigned int j=0;j<(*sSet)[i]->entry_elements;j++) + { + vSet.AddEntry((*sSet)[i]->entry[j]); + } + } + vSet.current = (*sSet)[i]->current; + vecSet->push_back(vSet); + } + } + + static void FreeStruct(unsigned int iElements, ADDON_StructSetting*** sSet) + { + if(iElements == 0) + return; + + for(unsigned int i=0;itype == DllSetting::SPIN) + { + for(unsigned int j=0;j<(*sSet)[i]->entry_elements;j++) + { + free((*sSet)[i]->entry[j]); + } + free((*sSet)[i]->entry); + } + free((*sSet)[i]->id); + free((*sSet)[i]->label); + free((*sSet)[i]); + } + free(*sSet); + } +}; + +#endif diff --git a/xbmc/addons/include/xbmc_addon_dll.h b/xbmc/addons/include/xbmc_addon_dll.h new file mode 100644 index 0000000..fa6415f --- /dev/null +++ b/xbmc/addons/include/xbmc_addon_dll.h @@ -0,0 +1,55 @@ +#ifndef __XBMC_ADDON_DLL_H__ +#define __XBMC_ADDON_DLL_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifdef TARGET_WINDOWS +#include +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif + +#include "xbmc_addon_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + ADDON_STATUS __declspec(dllexport) ADDON_Create(void *callbacks, void* props); + void __declspec(dllexport) ADDON_Stop(); + void __declspec(dllexport) ADDON_Destroy(); + ADDON_STATUS __declspec(dllexport) ADDON_GetStatus(); + bool __declspec(dllexport) ADDON_HasSettings(); + unsigned int __declspec(dllexport) ADDON_GetSettings(ADDON_StructSetting ***sSet); + ADDON_STATUS __declspec(dllexport) ADDON_SetSetting(const char *settingName, const void *settingValue); + void __declspec(dllexport) ADDON_FreeSettings(); + void __declspec(dllexport) ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/xbmc/addons/include/xbmc_addon_types.h b/xbmc/addons/include/xbmc_addon_types.h new file mode 100644 index 0000000..bd6cbe8 --- /dev/null +++ b/xbmc/addons/include/xbmc_addon_types.h @@ -0,0 +1,64 @@ +#ifndef __XBMC_ADDON_TYPES_H__ +#define __XBMC_ADDON_TYPES_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +enum ADDON_STATUS +{ + ADDON_STATUS_OK, + ADDON_STATUS_LOST_CONNECTION, + ADDON_STATUS_NEED_RESTART, + ADDON_STATUS_NEED_SETTINGS, + ADDON_STATUS_UNKNOWN, + ADDON_STATUS_NEED_SAVEDSETTINGS, + ADDON_STATUS_PERMANENT_FAILURE /**< permanent failure, like failing to resolve methods */ +}; + +typedef struct +{ + int type; + char* id; + char* label; + int current; + char** entry; + unsigned int entry_elements; +} ADDON_StructSetting; + +/*! + * @brief Handle used to return data from the PVR add-on to CPVRClient + */ +struct ADDON_HANDLE_STRUCT +{ + void *callerAddress; /*!< address of the caller */ + void *dataAddress; /*!< address to store data in */ + int dataIdentifier; /*!< parameter to pass back when calling the callback */ +}; +typedef ADDON_HANDLE_STRUCT *ADDON_HANDLE; + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/xbmc/addons/include/xbmc_audioenc_dll.h b/xbmc/addons/include/xbmc_audioenc_dll.h new file mode 100644 index 0000000..01e8d12 --- /dev/null +++ b/xbmc/addons/include/xbmc_audioenc_dll.h @@ -0,0 +1,61 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifndef __XBMC_AUDIOENC_H__ +#define __XBMC_AUDIOENC_H__ + +#include +#include "xbmc_addon_dll.h" +#include "xbmc_audioenc_types.h" + +extern "C" +{ + //! \copydoc AudioEncoder::Create + void* Create(audioenc_callbacks *callbacks); + + //! \copydoc AudioEncoder::Start + bool Start(void* context, int iInChannels, int iInRate, int iInBits, + const char* title, const char* artist, + const char* albumartist, const char* album, + const char* year, const char* track, + const char* genre, const char* comment, int iTrackLength); + + //! \copydoc AudioEncoder::Encode + int Encode(void* context, int nNumBytesRead, uint8_t* pbtStream); + + //! \copydoc AudioEncoder::Finish + bool Finish(void* context); + + //! \copydoc AudioEncoder::Free + void Free(void* context); + + // function to export the above structure to XBMC + void __declspec(dllexport) get_addon(struct AudioEncoder* pScr) + { + pScr->Create = Create; + pScr->Start = Start; + pScr->Encode = Encode; + pScr->Finish = Finish; + pScr->Free = Free; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_audioenc_types.h b/xbmc/addons/include/xbmc_audioenc_types.h new file mode 100644 index 0000000..aa527db --- /dev/null +++ b/xbmc/addons/include/xbmc_audioenc_types.h @@ -0,0 +1,113 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifndef __AUDIOENC_TYPES_H__ +#define __AUDIOENC_TYPES_H__ + +#ifdef TARGET_WINDOWS +#include +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif + +#include + +extern "C" +{ + struct AUDIOENC_INFO + { + int dummy; + }; + + struct AUDIOENC_PROPS + { + int dummy; + }; + + typedef int (*audioenc_write_callback)(void* opaque, uint8_t* data, int len); + typedef int64_t (*audioenc_seek_callback)(void* opaque, int64_t pos, int whence); + + typedef struct + { + void* opaque; + audioenc_write_callback write; + audioenc_seek_callback seek; + } audioenc_callbacks; + + struct AudioEncoder + { + /*! \brief Create encoder context + \param callbacks Pointer to audioenc_callbacks structure. + \return opaque pointer to encoder context, to be passed to other methods. + \sa IEncoder::Init + */ + void (*(__cdecl *Create) (audioenc_callbacks* callbacks)); + + /*! \brief Start encoder + \param context Encoder context from Create. + \param iInChannels Number of channels + \param iInRate Sample rate of input data + \param iInBits Bits per sample in input data + \param title The title of the song + \param artist The artist of the song + \param albumartist The albumartist of the song + \param year The year of the song + \param track The track number of the song + \param genre The genre of the song + \param comment A comment to attach to the song + \param iTrackLength Total track length in seconds + \sa IEncoder::Init + */ + bool (__cdecl* Start) (void* context, int iInChannels, int iInRate, int iInBits, + const char* title, const char* artist, + const char* albumartist, const char* album, + const char* year, const char* track, + const char* genre, const char* comment, + int iTrackLength); + + /*! \brief Encode a chunk of audio + \param context Encoder context from Create. + \param nNumBytesRead Number of bytes in input buffer + \param pbtStream the input buffer + \return Number of bytes consumed + \sa IEncoder::Encode + */ + int (__cdecl* Encode) (void* context, int nNumBytesRead, uint8_t* pbtStream); + + /*! \brief Finalize encoding + \param context Encoder context from Create. + \return True on success, false on failure. + */ + bool (__cdecl* Finish) (void* context); + + /*! \brief Free encoder context + \param context Encoder context to free. + */ + void (__cdecl* Free)(void* context); + }; +} + +#endif diff --git a/xbmc/addons/include/xbmc_codec_types.h b/xbmc/addons/include/xbmc_codec_types.h new file mode 100644 index 0000000..98003e0 --- /dev/null +++ b/xbmc/addons/include/xbmc_codec_types.h @@ -0,0 +1,55 @@ +#ifndef __XBMC_CODEC_TYPES_H__ +#define __XBMC_CODEC_TYPES_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int xbmc_codec_id_t; + +typedef enum +{ + XBMC_CODEC_TYPE_UNKNOWN = -1, + XBMC_CODEC_TYPE_VIDEO, + XBMC_CODEC_TYPE_AUDIO, + XBMC_CODEC_TYPE_DATA, + XBMC_CODEC_TYPE_SUBTITLE, + XBMC_CODEC_TYPE_RDS, + XBMC_CODEC_TYPE_NB +} xbmc_codec_type_t; + +typedef struct +{ + xbmc_codec_type_t codec_type; + xbmc_codec_id_t codec_id; +} xbmc_codec_t; + +#define XBMC_INVALID_CODEC_ID 0 +#define XBMC_INVALID_CODEC { XBMC_CODEC_TYPE_UNKNOWN, XBMC_INVALID_CODEC_ID } + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/xbmc/addons/include/xbmc_epg_types.h b/xbmc/addons/include/xbmc_epg_types.h new file mode 100644 index 0000000..97cea40 --- /dev/null +++ b/xbmc/addons/include/xbmc_epg_types.h @@ -0,0 +1,96 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include + +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if defined(__GNUC__) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +/*! @name EPG entry content event types */ +//@{ +/* These IDs come from the DVB-SI EIT table "content descriptor" + * Also known under the name "E-book genre assignments" + */ +#define EPG_EVENT_CONTENTMASK_UNDEFINED 0x00 +#define EPG_EVENT_CONTENTMASK_MOVIEDRAMA 0x10 +#define EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS 0x20 +#define EPG_EVENT_CONTENTMASK_SHOW 0x30 +#define EPG_EVENT_CONTENTMASK_SPORTS 0x40 +#define EPG_EVENT_CONTENTMASK_CHILDRENYOUTH 0x50 +#define EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE 0x60 +#define EPG_EVENT_CONTENTMASK_ARTSCULTURE 0x70 +#define EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS 0x80 +#define EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE 0x90 +#define EPG_EVENT_CONTENTMASK_LEISUREHOBBIES 0xA0 +#define EPG_EVENT_CONTENTMASK_SPECIAL 0xB0 +#define EPG_EVENT_CONTENTMASK_USERDEFINED 0xF0 +//@} + +/* Set EPGTAG.iGenreType to EPG_GENRE_USE_STRING to transfer genre strings to XBMC */ +#define EPG_GENRE_USE_STRING 0x100 + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + * @brief Representation of an EPG event. + */ + typedef struct EPG_TAG { + unsigned int iUniqueBroadcastId; /*!< @brief (required) identifier for this event */ + const char * strTitle; /*!< @brief (required) this event's title */ + unsigned int iChannelNumber; /*!< @brief (required) the number of the channel this event occurs on */ + time_t startTime; /*!< @brief (required) start time in UTC */ + time_t endTime; /*!< @brief (required) end time in UTC */ + const char * strPlotOutline; /*!< @brief (optional) plot outline */ + const char * strPlot; /*!< @brief (optional) plot */ + const char * strIconPath; /*!< @brief (optional) icon path */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + const char * strGenreDescription; /*!< @brief (optional) genre. Will be used only when iGenreType = EPG_GENRE_USE_STRING */ + time_t firstAired; /*!< @brief (optional) first aired in UTC */ + int iParentalRating; /*!< @brief (optional) parental rating */ + int iStarRating; /*!< @brief (optional) star rating */ + bool bNotify; /*!< @brief (optional) notify the user when this event starts */ + int iSeriesNumber; /*!< @brief (optional) series number */ + int iEpisodeNumber; /*!< @brief (optional) episode number */ + int iEpisodePartNumber; /*!< @brief (optional) episode part number */ + const char * strEpisodeName; /*!< @brief (optional) episode name */ + const char * strRecordingId; /*!< @brief (optional) unique id of the recording on the client which represents this event */ + } ATTRIBUTE_PACKED EPG_TAG; + +#ifdef __cplusplus +} +#endif diff --git a/xbmc/addons/include/xbmc_pvr_dll.h b/xbmc/addons/include/xbmc_pvr_dll.h new file mode 100644 index 0000000..3ad46fc --- /dev/null +++ b/xbmc/addons/include/xbmc_pvr_dll.h @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifndef __XBMC_PVR_H__ +#define __XBMC_PVR_H__ + +#include "xbmc_addon_dll.h" +#include "xbmc_pvr_types.h" + +/*! + * Functions that the PVR client add-on must implement, but some can be empty. + * + * The 'remarks' field indicates which methods should be implemented, and which ones are optional. + */ + +extern "C" +{ + /*! @name PVR add-on methods */ + //@{ + /*! + * Get the XBMC_PVR_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_PVR_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetPVRAPIVersion(void); + + /*! + * Get the XBMC_PVR_MIN_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_PVR_MIN_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetMininumPVRAPIVersion(void); + + /*! + * Get the XBMC_GUI_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_GUI_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetGUIAPIVersion(void); + + /*! + * Get the XBMC_GUI_MIN_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_GUI_MIN_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetMininumGUIAPIVersion(void); + + /*! + * Get the list of features that this add-on provides. + * Called by XBMC to query the add-on's capabilities. + * Used to check which options should be presented in the UI, which methods to call, etc. + * All capabilities that the add-on supports should be set to true. + * @param pCapabilities The add-on's capabilities. + * @return PVR_ERROR_NO_ERROR if the properties were fetched successfully. + * @remarks Valid implementation required. + */ + PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities); + + /*! + * @return The name reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetBackendName(void); + + /*! + * @return The version string reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetBackendVersion(void); + + /*! + * @return The connection string reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetConnectionString(void); + + /*! + * Get the disk space reported by the backend (if supported). + * @param iTotal The total disk space in bytes. + * @param iUsed The used disk space in bytes. + * @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed); + + /*! + * Call one of the menu hooks (if supported). + * Supported PVR_MENUHOOK instances have to be added in ADDON_Create(), by calling AddMenuHook() on the callback. + * @param menuhook The hook to call. + * @param item The selected item for which the hook was called. + * @return PVR_ERROR_NO_ERROR if the hook was called successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA &item); + //@} + + /*! @name PVR EPG methods + * @remarks Only used by XBMC if bSupportsEPG is set to true. + */ + //@{ + /*! + * Request the EPG for a channel from the backend. + * EPG entries are added to XBMC by calling TransferEpgEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param channel The channel to get the EPG table for. + * @param iStart Get events after this time (UTC). + * @param iEnd Get events before this time (UTC). + * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. + * @remarks Required if bSupportsEPG is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL& channel, time_t iStart, time_t iEnd); + //@} + + /*! @name PVR channel group methods + * @remarks Only used by XBMC is bSupportsChannelGroups is set to true. + * If a group or one of the group members changes after the initial import, or if a new one was added, then the add-on + * should call TriggerChannelGroupsUpdate() + */ + //@{ + /*! + * Get the total amount of channel groups on the backend if it supports channel groups. + * @return The amount of channels, or -1 on error. + * @remarks Required if bSupportsChannelGroups is set to true. Return -1 if this add-on won't provide this function. + */ + int GetChannelGroupsAmount(void); + + /*! + * Request the list of all channel groups from the backend if it supports channel groups. + * Channel group entries are added to XBMC by calling TransferChannelGroup() on the callback. + * @param handle Handle to pass to the callback method. + * @param bRadio True to get the radio channel groups, false to get the TV channel groups. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsChannelGroups is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio); + + /*! + * Request the list of all group members of a group from the backend if it supports channel groups. + * Member entries are added to XBMC by calling TransferChannelGroupMember() on the callback. + * @param handle Handle to pass to the callback method. + * @param group The group to get the members for. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsChannelGroups is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group); + //@} + + /** @name PVR channel methods + * @remarks Either bSupportsTV or bSupportsRadio is required to be set to true. + * If a channel changes after the initial import, or if a new one was added, then the add-on + * should call TriggerChannelUpdate() + */ + //@{ + /*! + * Show the channel scan dialog if this backend supports it. + * @return PVR_ERROR_NO_ERROR if the dialog was displayed successfully. + * @remarks Required if bSupportsChannelScan is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + * @note see libXBMC_gui.h about related parts + */ + PVR_ERROR OpenDialogChannelScan(void); + + /*! + * @return The total amount of channels on the backend, or -1 on error. + * @remarks Valid implementation required. + */ + int GetChannelsAmount(void); + + /*! + * Request the list of all channels from the backend. + * Channel entries are added to XBMC by calling TransferChannelEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param bRadio True to get the radio channels, false to get the TV channels. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks If bSupportsTV is set to true, a valid result set needs to be provided for bRadio = false. + * If bSupportsRadio is set to true, a valid result set needs to be provided for bRadio = true. + * At least one of these two must provide a valid result set. + */ + PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio); + + /*! + * Delete a channel from the backend. + * @param channel The channel to delete. + * @return PVR_ERROR_NO_ERROR if the channel has been deleted successfully. + * @remarks Required if bSupportsChannelSettings is set to true. + */ + PVR_ERROR DeleteChannel(const PVR_CHANNEL& channel); + + /*! + * Rename a channel on the backend. + * @param channel The channel to rename, containing the new channel name. + * @return PVR_ERROR_NO_ERROR if the channel has been renamed successfully. + * @remarks Optional, and only used if bSupportsChannelSettings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR RenameChannel(const PVR_CHANNEL& channel); + + /*! + * Move a channel to another channel number on the backend. + * @param channel The channel to move, containing the new channel number. + * @return PVR_ERROR_NO_ERROR if the channel has been moved successfully. + * @remarks Optional, and only used if bSupportsChannelSettings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR MoveChannel(const PVR_CHANNEL& channel); + + /*! + * Show the channel settings dialog, if supported by the backend. + * @param channel The channel to show the dialog for. + * @return PVR_ERROR_NO_ERROR if the dialog has been displayed successfully. + * @remarks Required if bSupportsChannelSettings is set to true. + * @note see libXBMC_gui.h about related parts + */ + PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL& channel); + + /*! + * Show the dialog to add a channel on the backend, if supported by the backend. + * @param channel The channel to add. + * @return PVR_ERROR_NO_ERROR if the channel has been added successfully. + * @remarks Required if bSupportsChannelSettings is set to true. + * @note see libXBMC_gui.h about related parts + */ + PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel); + //@} + + /** @name PVR recording methods + * @remarks Only used by XBMC is bSupportsRecordings is set to true. + * If a recording changes after the initial import, or if a new one was added, + * then the add-on should call TriggerRecordingUpdate() + */ + //@{ + /*! + * @return The total amount of recordings on the backend or -1 on error. + * @param deleted if set return deleted recording (called if bSupportsRecordingsUndelete set to true) + * @remarks Required if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + int GetRecordingsAmount(bool deleted); + + /*! + * Request the list of all recordings from the backend, if supported. + * Recording entries are added to XBMC by calling TransferRecordingEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param deleted if set return deleted recording (called if bSupportsRecordingsUndelete set to true) + * @return PVR_ERROR_NO_ERROR if the recordings have been fetched successfully. + * @remarks Required if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted); + + /*! + * Delete a recording on the backend. + * @param recording The recording to delete. + * @return PVR_ERROR_NO_ERROR if the recording has been deleted successfully. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteRecording(const PVR_RECORDING& recording); + + /*! + * Undelete a recording on the backend. + * @param recording The recording to undelete. + * @return PVR_ERROR_NO_ERROR if the recording has been undeleted successfully. + * @remarks Optional, and only used if bSupportsRecordingsUndelete is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording); + + /*! + * @brief Delete all recordings permanent which in the deleted folder on the backend. + * @return PVR_ERROR_NO_ERROR if the recordings has been deleted successfully. + */ + PVR_ERROR DeleteAllRecordingsFromTrash(); + + /*! + * Rename a recording on the backend. + * @param recording The recording to rename, containing the new name. + * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR RenameRecording(const PVR_RECORDING& recording); + + /*! + * Set the play count of a recording on the backend. + * @param recording The recording to change the play count. + * @param count Play count. + * @return PVR_ERROR_NO_ERROR if the recording's play count has been set successfully. + * @remarks Required if bSupportsRecordingPlayCount is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count); + + /*! + * Set the last watched position of a recording on the backend. + * @param recording The recording. + * @param position The last watched position in seconds + * @return PVR_ERROR_NO_ERROR if the position has been stored successfully. + * @remarks Required if bSupportsLastPlayedPosition is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition); + + /*! + * Retrieve the last watched position of a recording on the backend. + * @param recording The recording. + * @return The last watched position in seconds or -1 on error + * @remarks Required if bSupportsRecordingPlayCount is set to true. Return -1 if this add-on won't provide this function. + */ + int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording); + + /*! + * Retrieve the edit decision list (EDL) of a recording on the backend. + * @param recording The recording. + * @param edl out: The function has to write the EDL list into this array. + * @param size in: The maximum size of the EDL, out: the actual size of the EDL. + * @return PVR_ERROR_NO_ERROR if the EDL was successfully read. + * @remarks Required if bSupportsRecordingEdl is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY edl[], int *size); + + //@} + /** @name PVR timer methods + * @remarks Only used by XBMC is bSupportsTimers is set to true. + * If a timer changes after the initial import, or if a new one was added, + * then the add-on should call TriggerTimerUpdate() + */ + //@{ + /*! + * @return The total amount of timers on the backend or -1 on error. + * @remarks Required if bSupportsTimers is set to true. Return -1 if this add-on won't provide this function. + */ + int GetTimersAmount(void); + + /*! + * Request the list of all timers from the backend if supported. + * Timer entries are added to XBMC by calling TransferTimerEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetTimers(ADDON_HANDLE handle); + + /*! + * Add a timer on the backend. + * @param timer The timer to add. + * @return PVR_ERROR_NO_ERROR if the timer has been added successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR AddTimer(const PVR_TIMER& timer); + + /*! + * Delete a timer on the backend. + * @param timer The timer to delete. + * @param bForceDelete Set to true to delete a timer that is currently recording a program. + * @return PVR_ERROR_NO_ERROR if the timer has been deleted successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete); + + /*! + * Update the timer information on the backend. + * @param timer The timer to update. + * @return PVR_ERROR_NO_ERROR if the timer has been updated successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR UpdateTimer(const PVR_TIMER& timer); + + //@} + + /** @name PVR live stream methods, used to open and close a stream to a channel, and optionally perform read operations on the stream */ + //@{ + /*! + * Open a live stream on the backend. + * @param channel The channel to stream. + * @return True if the stream has been opened successfully, false otherwise. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return false if this add-on won't provide this function. + */ + bool OpenLiveStream(const PVR_CHANNEL& channel); + + /*! + * Close an open live stream. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. + */ + void CloseLiveStream(void); + + /*! + * Read from an open live stream. + * @param pBuffer The buffer to store the data in. + * @param iBufferSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + * @remarks Required if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize); + + /*! + * Seek in a live stream on a backend that supports timeshifting. + * @param iPosition The position to seek to. + * @param iWhence ? + * @return The new position. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long SeekLiveStream(long long iPosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long PositionLiveStream(void); + + /*! + * @return The total length of the stream that's currently being read. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long LengthLiveStream(void); + + /*! + * @return The channel number on the backend of the live stream that's currently being read. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return -1 if this add-on won't provide this function. + */ + int GetCurrentClientChannel(void); + + /*! + * Switch to another channel. Only to be called when a live stream has already been opened. + * @param channel The channel to switch to. + * @return True if the switch was successful, false otherwise. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return false if this add-on won't provide this function. + */ + bool SwitchChannel(const PVR_CHANNEL& channel); + + /*! + * Get the signal status of the stream that's currently open. + * @param signalStatus The signal status. + * @return True if the signal status has been read successfully, false otherwise. + * @remarks Optional, and only used if bHandlesInputStream or bHandlesDemuxing is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS& signalStatus); + + /*! + * Get the stream URL for a channel from the backend. Used by the MediaPortal add-on. + * @param channel The channel to get the stream URL for. + * @return The requested URL. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return NULL if this add-on won't provide this function. + */ + const char* GetLiveStreamURL(const PVR_CHANNEL& channel); + + /*! + * Get the stream properties of the stream that's currently being read. + * @param pProperties The properties of the currently playing stream. + * @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties); + //@} + + /** @name PVR recording stream methods, used to open and close a stream to a recording, and perform read operations on the stream. + * @remarks This will only be used if the backend doesn't provide a direct URL in the recording tag. + */ + //@{ + /*! + * Open a stream to a recording on the backend. + * @param recording The recording to open. + * @return True if the stream has been opened successfully, false otherwise. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return false if this add-on won't provide this function. + */ + bool OpenRecordedStream(const PVR_RECORDING& recording); + + /*! + * Close an open stream from a recording. + * @remarks Optional, and only used if bSupportsRecordings is set to true. + */ + void CloseRecordedStream(void); + + /*! + * Read from a recording. + * @param pBuffer The buffer to store the data in. + * @param iBufferSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + * @remarks Optional, and only used if bSupportsRecordings is set to true, but required if OpenRecordedStream() is implemented. Return -1 if this add-on won't provide this function. + */ + int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize); + + /*! + * Seek in a recorded stream. + * @param iPosition The position to seek to. + * @param iWhence ? + * @return The new position. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long SeekRecordedStream(long long iPosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long PositionRecordedStream(void); + + /*! + * @return The total length of the stream that's currently being read. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long LengthRecordedStream(void); + //@} + + /** @name PVR demultiplexer methods + * @remarks Only used by XBMC is bHandlesDemuxing is set to true. + */ + //@{ + /*! + * Reset the demultiplexer in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxReset(void); + + /*! + * Abort the demultiplexer thread in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxAbort(void); + + /*! + * Flush all data that's currently in the demultiplexer buffer in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxFlush(void); + + /*! + * Read the next packet from the demultiplexer, if there is one. + * @return The next packet. + * If there is no next packet, then the add-on should return the + * packet created by calling AllocateDemuxPacket(0) on the callback. + * If the stream changed and XBMC's player needs to be reinitialised, + * then, the add-on should call AllocateDemuxPacket(0) on the + * callback, and set the streamid to DMX_SPECIALID_STREAMCHANGE and + * return the value. + * The add-on should return NULL if an error occured. + * @remarks Required if bHandlesDemuxing is set to true. Return NULL if this add-on won't provide this function. + */ + DemuxPacket* DemuxRead(void); + //@} + + /*! + * Delay to use when using switching channels for add-ons not providing an input stream. + * If the add-on does provide an input stream, then this method will not be called. + * Those add-ons can do that in OpenLiveStream() if needed. + * @return The delay in milliseconds. + */ + unsigned int GetChannelSwitchDelay(void); + + /*! + * Check if the backend support pausing the currently playing stream + * This will enable/disable the pause button in XBMC based on the return value + * @return false if the PVR addon/backend does not support pausing, true if possible + */ + bool CanPauseStream(); + + /*! + * Check if the backend supports seeking for the currently playing stream + * This will enable/disable the rewind/forward buttons in XBMC based on the return value + * @return false if the PVR addon/backend does not support seeking, true if possible + */ + bool CanSeekStream(); + + /*! + * @brief Notify the pvr addon that XBMC (un)paused the currently playing stream + */ + void PauseStream(bool bPaused); + + /*! + * Notify the pvr addon/demuxer that XBMC wishes to seek the stream by time + * @param time The absolute time since stream start + * @param backwards True to seek to keyframe BEFORE time, else AFTER + * @param startpts can be updated to point to where display should start + * @return True if the seek operation was possible + * @remarks Optional, and only used if addon has its own demuxer. Return False if this add-on won't provide this function. + */ + bool SeekTime(int time, bool backwards, double *startpts); + + /*! + * Notify the pvr addon/demuxer that XBMC wishes to change playback speed + * @param speed The requested playback speed + * @remarks Optional, and only used if addon has its own demuxer. + */ + void SetSpeed(int speed); + + /*! + * Get actual playing time from addon. With timeshift enabled this is + * different to live. + * @return time as UTC + */ + time_t GetPlayingTime(); + + /*! + * Get time of oldest packet in timeshift buffer + * @return time as UTC + */ + time_t GetBufferTimeStart(); + + /*! + * Get time of latest packet in timeshift buffer + * @return time as UTC + */ + time_t GetBufferTimeEnd(); + + /*! + * Get the hostname of the pvr backend server + * @return hostname as ip address or alias. If backend does not + * utilize a server, return empty string. + */ + const char* GetBackendHostname(); + + /*! + * Called by XBMC to assign the function pointers of this add-on to pClient. + * @param pClient The struct to assign the function pointers to. + */ + void __declspec(dllexport) get_addon(struct PVRClient* pClient) + { + pClient->GetPVRAPIVersion = GetPVRAPIVersion; + pClient->GetMininumPVRAPIVersion = GetMininumPVRAPIVersion; + pClient->GetGUIAPIVersion = GetGUIAPIVersion; + pClient->GetMininumGUIAPIVersion = GetMininumGUIAPIVersion; + pClient->GetAddonCapabilities = GetAddonCapabilities; + pClient->GetStreamProperties = GetStreamProperties; + pClient->GetConnectionString = GetConnectionString; + pClient->GetBackendName = GetBackendName; + pClient->GetBackendVersion = GetBackendVersion; + pClient->GetDriveSpace = GetDriveSpace; + pClient->OpenDialogChannelScan = OpenDialogChannelScan; + pClient->MenuHook = CallMenuHook; + + pClient->GetEpg = GetEPGForChannel; + + pClient->GetChannelGroupsAmount = GetChannelGroupsAmount; + pClient->GetChannelGroups = GetChannelGroups; + pClient->GetChannelGroupMembers = GetChannelGroupMembers; + + pClient->GetChannelsAmount = GetChannelsAmount; + pClient->GetChannels = GetChannels; + pClient->DeleteChannel = DeleteChannel; + pClient->RenameChannel = RenameChannel; + pClient->MoveChannel = MoveChannel; + pClient->OpenDialogChannelSettings = OpenDialogChannelSettings; + pClient->OpenDialogChannelAdd = OpenDialogChannelAdd; + + pClient->GetRecordingsAmount = GetRecordingsAmount; + pClient->GetRecordings = GetRecordings; + pClient->DeleteRecording = DeleteRecording; + pClient->UndeleteRecording = UndeleteRecording; + pClient->DeleteAllRecordingsFromTrash = DeleteAllRecordingsFromTrash; + pClient->RenameRecording = RenameRecording; + pClient->SetRecordingPlayCount = SetRecordingPlayCount; + pClient->SetRecordingLastPlayedPosition = SetRecordingLastPlayedPosition; + pClient->GetRecordingLastPlayedPosition = GetRecordingLastPlayedPosition; + pClient->GetRecordingEdl = GetRecordingEdl; + + pClient->GetTimersAmount = GetTimersAmount; + pClient->GetTimers = GetTimers; + pClient->AddTimer = AddTimer; + pClient->DeleteTimer = DeleteTimer; + pClient->UpdateTimer = UpdateTimer; + + pClient->OpenLiveStream = OpenLiveStream; + pClient->CloseLiveStream = CloseLiveStream; + pClient->ReadLiveStream = ReadLiveStream; + pClient->SeekLiveStream = SeekLiveStream; + pClient->PositionLiveStream = PositionLiveStream; + pClient->LengthLiveStream = LengthLiveStream; + pClient->GetCurrentClientChannel = GetCurrentClientChannel; + pClient->SwitchChannel = SwitchChannel; + pClient->SignalStatus = SignalStatus; + pClient->GetLiveStreamURL = GetLiveStreamURL; + pClient->GetChannelSwitchDelay = GetChannelSwitchDelay; + pClient->CanPauseStream = CanPauseStream; + pClient->PauseStream = PauseStream; + pClient->CanSeekStream = CanSeekStream; + pClient->SeekTime = SeekTime; + pClient->SetSpeed = SetSpeed; + + pClient->OpenRecordedStream = OpenRecordedStream; + pClient->CloseRecordedStream = CloseRecordedStream; + pClient->ReadRecordedStream = ReadRecordedStream; + pClient->SeekRecordedStream = SeekRecordedStream; + pClient->PositionRecordedStream = PositionRecordedStream; + pClient->LengthRecordedStream = LengthRecordedStream; + + pClient->DemuxReset = DemuxReset; + pClient->DemuxAbort = DemuxAbort; + pClient->DemuxFlush = DemuxFlush; + pClient->DemuxRead = DemuxRead; + + pClient->GetPlayingTime = GetPlayingTime; + pClient->GetBufferTimeStart = GetBufferTimeStart; + pClient->GetBufferTimeEnd = GetBufferTimeEnd; + + pClient->GetBackendHostname = GetBackendHostname; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h new file mode 100644 index 0000000..5285bd1 --- /dev/null +++ b/xbmc/addons/include/xbmc_pvr_types.h @@ -0,0 +1,415 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifndef __PVRCLIENT_TYPES_H__ +#define __PVRCLIENT_TYPES_H__ + +#ifdef TARGET_WINDOWS +#include +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif +#include +#include + +#include "xbmc_addon_types.h" +#include "xbmc_epg_types.h" +#include "xbmc_codec_types.h" + +/*! @note Define "USE_DEMUX" at compile time if demuxing in the PVR add-on is used. + * Also XBMC's "DVDDemuxPacket.h" file must be in the include path of the add-on, + * and the add-on should set bHandlesDemuxing to true. + */ +#ifdef USE_DEMUX +#include "DVDDemuxPacket.h" +#else +struct DemuxPacket; +#endif + +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if defined(__GNUC__) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +#define PVR_ADDON_NAME_STRING_LENGTH 1024 +#define PVR_ADDON_URL_STRING_LENGTH 1024 +#define PVR_ADDON_DESC_STRING_LENGTH 1024 +#define PVR_ADDON_INPUT_FORMAT_STRING_LENGTH 32 +#define PVR_ADDON_EDL_LENGTH 32 + +/* using the default avformat's MAX_STREAMS value to be safe */ +#define PVR_STREAM_MAX_STREAMS 20 + +/* current PVR API version */ +#define XBMC_PVR_API_VERSION "1.9.4" + +/* min. PVR API version */ +#define XBMC_PVR_MIN_API_VERSION "1.9.4" + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + * @brief PVR add-on error codes + */ + typedef enum + { + PVR_ERROR_NO_ERROR = 0, /*!< @brief no error occurred */ + PVR_ERROR_UNKNOWN = -1, /*!< @brief an unknown error occurred */ + PVR_ERROR_NOT_IMPLEMENTED = -2, /*!< @brief the method that XBMC called is not implemented by the add-on */ + PVR_ERROR_SERVER_ERROR = -3, /*!< @brief the backend reported an error, or the add-on isn't connected */ + PVR_ERROR_SERVER_TIMEOUT = -4, /*!< @brief the command was sent to the backend, but the response timed out */ + PVR_ERROR_REJECTED = -5, /*!< @brief the command was rejected by the backend */ + PVR_ERROR_ALREADY_PRESENT = -6, /*!< @brief the requested item can not be added, because it's already present */ + PVR_ERROR_INVALID_PARAMETERS = -7, /*!< @brief the parameters of the method that was called are invalid for this operation */ + PVR_ERROR_RECORDING_RUNNING = -8, /*!< @brief a recording is running, so the timer can't be deleted without doing a forced delete */ + PVR_ERROR_FAILED = -9, /*!< @brief the command failed */ + } PVR_ERROR; + + /*! + * @brief PVR timer states + */ + typedef enum + { + PVR_TIMER_STATE_NEW = 0, /*!< @brief a new, unsaved timer */ + PVR_TIMER_STATE_SCHEDULED = 1, /*!< @brief the timer is scheduled for recording */ + PVR_TIMER_STATE_RECORDING = 2, /*!< @brief the timer is currently recordings */ + PVR_TIMER_STATE_COMPLETED = 3, /*!< @brief the recording completed successfully */ + PVR_TIMER_STATE_ABORTED = 4, /*!< @brief recording started, but was aborted */ + PVR_TIMER_STATE_CANCELLED = 5, /*!< @brief the timer was scheduled, but was canceled */ + PVR_TIMER_STATE_CONFLICT_OK = 6, /*!< @brief the scheduled timer conflicts with another one, but will be recorded */ + PVR_TIMER_STATE_CONFLICT_NOK = 7, /*!< @brief the scheduled timer conflicts with another one and won't be recorded */ + PVR_TIMER_STATE_ERROR = 8 /*!< @brief the timer is scheduled, but can't be recorded for some reason */ + } PVR_TIMER_STATE; + + /*! + * @brief PVR menu hook categories + */ + typedef enum + { + PVR_MENUHOOK_UNKNOWN =-1, /*!< @brief unknown menu hook */ + PVR_MENUHOOK_ALL = 0, /*!< @brief all categories */ + PVR_MENUHOOK_CHANNEL = 1, /*!< @brief for channels */ + PVR_MENUHOOK_TIMER = 2, /*!< @brief for timers */ + PVR_MENUHOOK_EPG = 3, /*!< @brief for EPG */ + PVR_MENUHOOK_RECORDING = 4, /*!< @brief for recordings */ + PVR_MENUHOOK_DELETED_RECORDING = 5, /*!< @brief for deleted recordings */ + PVR_MENUHOOK_SETTING = 6, /*!< @brief for settings */ + } PVR_MENUHOOK_CAT; + + /*! + * @brief Properties passed to the Create() method of an add-on. + */ + typedef struct PVR_PROPERTIES + { + const char* strUserPath; /*!< @brief path to the user profile */ + const char* strClientPath; /*!< @brief path to this add-on */ + } PVR_PROPERTIES; + + /*! + * @brief PVR add-on capabilities. All capabilities are set to "false" as default. + * If a capabilty is set to true, then the corresponding methods from xbmc_pvr_dll.h need to be implemented. + */ + typedef struct PVR_ADDON_CAPABILITIES + { + bool bSupportsEPG; /*!< @brief true if the add-on provides EPG information */ + bool bSupportsTV; /*!< @brief true if this add-on provides TV channels */ + bool bSupportsRadio; /*!< @brief true if this add-on supports radio channels */ + bool bSupportsRecordings; /*!< @brief true if this add-on supports playback of recordings stored on the backend */ + bool bSupportsRecordingsUndelete; /*!< @brief true if this add-on supports undelete of recordings stored on the backend */ + bool bSupportsTimers; /*!< @brief true if this add-on supports the creation and editing of timers */ + bool bSupportsChannelGroups; /*!< @brief true if this add-on supports channel groups */ + bool bSupportsChannelScan; /*!< @brief true if this add-on support scanning for new channels on the backend */ + bool bSupportsChannelSettings; /*!< @brief true if this add-on supports the following functions: DeleteChannel, RenameChannel, MoveChannel, DialogChannelSettings and DialogAddChannel */ + bool bHandlesInputStream; /*!< @brief true if this add-on provides an input stream. false if XBMC handles the stream. */ + bool bHandlesDemuxing; /*!< @brief true if this add-on demultiplexes packets. */ + bool bSupportsRecordingFolders; /*!< @brief true if the backend supports timers / recordings in folders. */ + bool bSupportsRecordingPlayCount; /*!< @brief true if the backend supports play count for recordings. */ + bool bSupportsLastPlayedPosition; /*!< @brief true if the backend supports store/retrieve of last played position for recordings. */ + bool bSupportsRecordingEdl; /*!< @brief true if the backend supports retrieving an edit decision list for recordings. */ + } ATTRIBUTE_PACKED PVR_ADDON_CAPABILITIES; + + /*! + * @brief PVR stream properties + */ + typedef struct PVR_STREAM_PROPERTIES + { + unsigned int iStreamCount; + struct PVR_STREAM + { + unsigned int iPhysicalId; /*!< @brief (required) physical index */ + xbmc_codec_type_t iCodecType; /*!< @brief (required) codec type this stream */ + xbmc_codec_id_t iCodecId; /*!< @brief (required) codec id of this stream */ + char strLanguage[4]; /*!< @brief (required) language id */ + int iIdentifier; /*!< @brief (required) stream id */ + int iFPSScale; /*!< @brief (required) scale of 1000 and a rate of 29970 will result in 29.97 fps */ + int iFPSRate; /*!< @brief (required) FPS rate */ + int iHeight; /*!< @brief (required) height of the stream reported by the demuxer */ + int iWidth; /*!< @brief (required) width of the stream reported by the demuxer */ + float fAspect; /*!< @brief (required) display aspect ratio of the stream */ + int iChannels; /*!< @brief (required) amount of channels */ + int iSampleRate; /*!< @brief (required) sample rate */ + int iBlockAlign; /*!< @brief (required) block alignment */ + int iBitRate; /*!< @brief (required) bit rate */ + int iBitsPerSample; /*!< @brief (required) bits per sample */ + } stream[PVR_STREAM_MAX_STREAMS]; /*!< @brief (required) the streams */ + } ATTRIBUTE_PACKED PVR_STREAM_PROPERTIES; + + /*! + * @brief Signal status information + */ + typedef struct PVR_SIGNAL_STATUS + { + char strAdapterName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the adapter that's being used */ + char strAdapterStatus[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) status of the adapter that's being used */ + char strServiceName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the current service */ + char strProviderName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the current service's provider */ + char strMuxName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the current mux */ + int iSNR; /*!< @brief (optional) signal/noise ratio */ + int iSignal; /*!< @brief (optional) signal strength */ + long iBER; /*!< @brief (optional) bit error rate */ + long iUNC; /*!< @brief (optional) uncorrected blocks */ + double dVideoBitrate; /*!< @brief (optional) video bitrate */ + double dAudioBitrate; /*!< @brief (optional) audio bitrate */ + double dDolbyBitrate; /*!< @brief (optional) dolby bitrate */ + } ATTRIBUTE_PACKED PVR_SIGNAL_STATUS; + + /*! + * @brief Menu hooks that are available in the context menus while playing a stream via this add-on. + * And in the Live TV settings dialog + */ + typedef struct PVR_MENUHOOK + { + unsigned int iHookId; /*!< @brief (required) this hook's identifier */ + unsigned int iLocalizedStringId; /*!< @brief (required) the id of the label for this hook in g_localizeStrings */ + PVR_MENUHOOK_CAT category; /*!< @brief (required) category of menu hook */ + } ATTRIBUTE_PACKED PVR_MENUHOOK; + + /*! + * @brief Representation of a TV or radio channel. + */ + typedef struct PVR_CHANNEL + { + unsigned int iUniqueId; /*!< @brief (required) unique identifier for this channel */ + bool bIsRadio; /*!< @brief (required) true if this is a radio channel, false if it's a TV channel */ + unsigned int iChannelNumber; /*!< @brief (optional) channel number of this channel on the backend */ + unsigned int iSubChannelNumber; /*!< @brief (optional) sub channel number of this channel on the backend (ATSC) */ + char strChannelName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) channel name given to this channel */ + char strInputFormat[PVR_ADDON_INPUT_FORMAT_STRING_LENGTH]; /*!< @brief (optional) input format type. types can be found in ffmpeg/libavformat/allformats.c + leave empty if unknown */ + char strStreamURL[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) the URL to use to access this channel. + leave empty to use this add-on to access the stream. + set to a path that's supported by XBMC otherwise. */ + unsigned int iEncryptionSystem; /*!< @brief (optional) the encryption ID or CaID of this channel */ + char strIconPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) path to the channel icon (if present) */ + bool bIsHidden; /*!< @brief (optional) true if this channel is marked as hidden */ + } ATTRIBUTE_PACKED PVR_CHANNEL; + + typedef struct PVR_CHANNEL_GROUP + { + char strGroupName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) name of this channel group */ + bool bIsRadio; /*!< @brief (required) true if this is a radio channel group, false otherwise. */ + } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP; + + typedef struct PVR_CHANNEL_GROUP_MEMBER + { + char strGroupName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) name of the channel group to add the channel to */ + unsigned int iChannelUniqueId; /*!< @brief (required) unique id of the member */ + unsigned int iChannelNumber; /*!< @brief (optional) channel number within the group */ + } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP_MEMBER; + + /*! + * @brief Representation of a timer event. + */ + typedef struct PVR_TIMER { + unsigned int iClientIndex; /*!< @brief (required) the index of this timer given by the client */ + int iClientChannelUid; /*!< @brief (required) unique identifier of the channel to record on */ + time_t startTime; /*!< @brief (required) start time of the recording in UTC. instant timers that are sent to the add-on by xbmc will have this value set to 0 */ + time_t endTime; /*!< @brief (required) end time of the recording in UTC */ + PVR_TIMER_STATE state; /*!< @brief (required) the state of this timer */ + char strTitle[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) title of this timer */ + char strDirectory[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) the directory where the recording will be stored in */ + char strSummary[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) the summary for this timer */ + int iPriority; /*!< @brief (optional) the priority of this timer */ + int iLifetime; /*!< @brief (optional) lifetimer of this timer in days */ + bool bIsRepeating; /*!< @brief (optional) true if this is a recurring timer */ + time_t firstDay; /*!< @brief (optional) the first day this recording is active in case of a repeating event */ + int iWeekdays; /*!< @brief (optional) weekday mask */ + int iEpgUid; /*!< @brief (optional) epg event id */ + unsigned int iMarginStart; /*!< @brief (optional) if set, the backend starts the recording iMarginStart minutes before startTime. */ + unsigned int iMarginEnd; /*!< @brief (optional) if set, the backend ends the recording iMarginEnd minutes after endTime. */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + } ATTRIBUTE_PACKED PVR_TIMER; + /*! + * @brief Representation of a recording. + */ + typedef struct PVR_RECORDING { + char strRecordingId[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) unique id of the recording on the client. */ + char strTitle[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) the title of this recording */ + char strStreamURL[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (required) stream URL to access this recording */ + char strDirectory[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) directory of this recording on the client */ + char strPlotOutline[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) plot outline */ + char strPlot[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) plot */ + char strChannelName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) channel name */ + char strIconPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) icon path */ + char strThumbnailPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) thumbnail path */ + char strFanartPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) fanart path */ + time_t recordingTime; /*!< @brief (optional) start time of the recording */ + int iDuration; /*!< @brief (optional) duration of the recording in seconds */ + int iPriority; /*!< @brief (optional) priority of this recording (from 0 - 100) */ + int iLifetime; /*!< @brief (optional) life time in days of this recording */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + int iPlayCount; /*!< @brief (optional) play count of this recording on the client */ + int iLastPlayedPosition; /*!< @brief (optional) last played position of this recording on the client */ + bool bIsDeleted; /*!< @brief (optional) shows this recording is deleted and can be undelete */ + } ATTRIBUTE_PACKED PVR_RECORDING; + + /*! + * @brief Edit definition list (EDL) + */ + typedef enum + { + PVR_EDL_TYPE_CUT = 0, /*!< @brief cut (completly remove content) */ + PVR_EDL_TYPE_MUTE = 1, /*!< @brief mute audio */ + PVR_EDL_TYPE_SCENE = 2, /*!< @brief scene markers (chapter seeking) */ + PVR_EDL_TYPE_COMBREAK = 3 /*!< @brief commercial breaks */ + } PVR_EDL_TYPE; + + typedef struct PVR_EDL_ENTRY + { + int64_t start; // ms + int64_t end; // ms + PVR_EDL_TYPE type; + } ATTRIBUTE_PACKED PVR_EDL_ENTRY; + + /*! + * @brief PVR menu hook data + */ + typedef struct PVR_MENUHOOK_DATA + { + PVR_MENUHOOK_CAT cat; + union data { + int iEpgUid; + PVR_CHANNEL channel; + PVR_TIMER timer; + PVR_RECORDING recording; + } data; + } ATTRIBUTE_PACKED PVR_MENUHOOK_DATA; + + /*! + * @brief Structure to transfer the methods from xbmc_pvr_dll.h to XBMC + */ + typedef struct PVRClient + { + const char* (__cdecl* GetPVRAPIVersion)(void); + const char* (__cdecl* GetMininumPVRAPIVersion)(void); + const char* (__cdecl* GetGUIAPIVersion)(void); + const char* (__cdecl* GetMininumGUIAPIVersion)(void); + PVR_ERROR (__cdecl* GetAddonCapabilities)(PVR_ADDON_CAPABILITIES*); + PVR_ERROR (__cdecl* GetStreamProperties)(PVR_STREAM_PROPERTIES*); + const char* (__cdecl* GetBackendName)(void); + const char* (__cdecl* GetBackendVersion)(void); + const char* (__cdecl* GetConnectionString)(void); + PVR_ERROR (__cdecl* GetDriveSpace)(long long*, long long*); + PVR_ERROR (__cdecl* MenuHook)(const PVR_MENUHOOK&, const PVR_MENUHOOK_DATA&); + PVR_ERROR (__cdecl* GetEpg)(ADDON_HANDLE, const PVR_CHANNEL&, time_t, time_t); + int (__cdecl* GetChannelGroupsAmount)(void); + PVR_ERROR (__cdecl* GetChannelGroups)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* GetChannelGroupMembers)(ADDON_HANDLE, const PVR_CHANNEL_GROUP&); + PVR_ERROR (__cdecl* OpenDialogChannelScan)(void); + int (__cdecl* GetChannelsAmount)(void); + PVR_ERROR (__cdecl* GetChannels)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* DeleteChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* RenameChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* MoveChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* OpenDialogChannelSettings)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* OpenDialogChannelAdd)(const PVR_CHANNEL&); + int (__cdecl* GetRecordingsAmount)(bool); + PVR_ERROR (__cdecl* GetRecordings)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* DeleteRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* UndeleteRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* DeleteAllRecordingsFromTrash)(void); + PVR_ERROR (__cdecl* RenameRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* SetRecordingPlayCount)(const PVR_RECORDING&, int); + PVR_ERROR (__cdecl* SetRecordingLastPlayedPosition)(const PVR_RECORDING&, int); + int (__cdecl* GetRecordingLastPlayedPosition)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* GetRecordingEdl)(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*); + int (__cdecl* GetTimersAmount)(void); + PVR_ERROR (__cdecl* GetTimers)(ADDON_HANDLE); + PVR_ERROR (__cdecl* AddTimer)(const PVR_TIMER&); + PVR_ERROR (__cdecl* DeleteTimer)(const PVR_TIMER&, bool); + PVR_ERROR (__cdecl* UpdateTimer)(const PVR_TIMER&); + bool (__cdecl* OpenLiveStream)(const PVR_CHANNEL&); + void (__cdecl* CloseLiveStream)(void); + int (__cdecl* ReadLiveStream)(unsigned char*, unsigned int); + long long (__cdecl* SeekLiveStream)(long long, int); + long long (__cdecl* PositionLiveStream)(void); + long long (__cdecl* LengthLiveStream)(void); + int (__cdecl* GetCurrentClientChannel)(void); + bool (__cdecl* SwitchChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* SignalStatus)(PVR_SIGNAL_STATUS&); + const char* (__cdecl* GetLiveStreamURL)(const PVR_CHANNEL&); + bool (__cdecl* OpenRecordedStream)(const PVR_RECORDING&); + void (__cdecl* CloseRecordedStream)(void); + int (__cdecl* ReadRecordedStream)(unsigned char*, unsigned int); + long long (__cdecl* SeekRecordedStream)(long long, int); + long long (__cdecl* PositionRecordedStream)(void); + long long (__cdecl* LengthRecordedStream)(void); + void (__cdecl* DemuxReset)(void); + void (__cdecl* DemuxAbort)(void); + void (__cdecl* DemuxFlush)(void); + DemuxPacket* (__cdecl* DemuxRead)(void); + unsigned int (__cdecl* GetChannelSwitchDelay)(void); + bool (__cdecl* CanPauseStream)(void); + void (__cdecl* PauseStream)(bool); + bool (__cdecl* CanSeekStream)(void); + bool (__cdecl* SeekTime)(int, bool, double*); + void (__cdecl* SetSpeed)(int); + time_t (__cdecl* GetPlayingTime)(void); + time_t (__cdecl* GetBufferTimeStart)(void); + time_t (__cdecl* GetBufferTimeEnd)(void); + const char* (__cdecl* GetBackendHostname)(void); + } PVRClient; + +#ifdef __cplusplus +} +#endif + +#endif //__PVRCLIENT_TYPES_H__ diff --git a/xbmc/addons/include/xbmc_scr_dll.h b/xbmc/addons/include/xbmc_scr_dll.h new file mode 100644 index 0000000..c4257b4 --- /dev/null +++ b/xbmc/addons/include/xbmc_scr_dll.h @@ -0,0 +1,45 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifndef __XBMC_SCR_H__ +#define __XBMC_SCR_H__ + +#include "xbmc_addon_dll.h" +#include "xbmc_scr_types.h" + +extern "C" +{ + + // Functions that your visualisation must implement + void Start(); + void Render(); + void GetInfo(SCR_INFO* pInfo); + + // function to export the above structure to XBMC + void __declspec(dllexport) get_addon(struct ScreenSaver* pScr) + { + pScr->Start = Start; + pScr->Render = Render; + pScr->GetInfo = GetInfo; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_scr_types.h b/xbmc/addons/include/xbmc_scr_types.h new file mode 100644 index 0000000..fec3040 --- /dev/null +++ b/xbmc/addons/include/xbmc_scr_types.h @@ -0,0 +1,53 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#ifndef __SCREENSAVER_TYPES_H__ +#define __SCREENSAVER_TYPES_H__ + +extern "C" +{ + struct SCR_INFO + { + int dummy; + }; + + struct SCR_PROPS + { + void *device; + int x; + int y; + int width; + int height; + float pixelRatio; + const char *name; + const char *presets; + const char *profile; + }; + + struct ScreenSaver + { + void (__cdecl* Start) (); + void (__cdecl* Render) (); + void (__cdecl* GetInfo)(SCR_INFO *info); + }; +} + +#endif // __SCREENSAVER_TYPES_H__ diff --git a/xbmc/addons/include/xbmc_stream_utils.hpp b/xbmc/addons/include/xbmc_stream_utils.hpp new file mode 100644 index 0000000..927fe33 --- /dev/null +++ b/xbmc/addons/include/xbmc_stream_utils.hpp @@ -0,0 +1,264 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "xbmc_pvr_types.h" +#include +#include + +namespace ADDON +{ + /** + * Represents a single stream. It extends the PODS to provide some operators + * overloads. + */ + class XbmcPvrStream : public PVR_STREAM_PROPERTIES::PVR_STREAM + { + public: + XbmcPvrStream() + { + Clear(); + } + + XbmcPvrStream(const XbmcPvrStream &other) + { + memcpy(this, &other, sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + } + + XbmcPvrStream& operator=(const XbmcPvrStream &other) + { + memcpy(this, &other, sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + return *this; + } + + /** + * Compares this stream based on another stream + * @param other + * @return + */ + inline bool operator==(const XbmcPvrStream &other) const + { + return iPhysicalId == other.iPhysicalId && iCodecId == other.iCodecId; + } + + /** + * Compares this stream with another one so that video streams are sorted + * before any other streams and the others are sorted by the physical ID + * @param other + * @return + */ + bool operator<(const XbmcPvrStream &other) const + { + if (iCodecType == XBMC_CODEC_TYPE_VIDEO) + return true; + else if (other.iCodecType != XBMC_CODEC_TYPE_VIDEO) + return iPhysicalId < other.iPhysicalId; + else + return false; + } + + /** + * Clears the stream + */ + void Clear() + { + memset(this, 0, sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + iCodecId = XBMC_INVALID_CODEC_ID; + iCodecType = XBMC_CODEC_TYPE_UNKNOWN; + } + + /** + * Checks whether the stream has been cleared + * @return + */ + inline bool IsCleared() const + { + return iCodecId == XBMC_INVALID_CODEC_ID && + iCodecType == XBMC_CODEC_TYPE_UNKNOWN; + } + }; + + class XbmcStreamProperties + { + public: + typedef std::vector stream_vector; + + XbmcStreamProperties(void) + { + // make sure the vector won't have to resize itself later + m_streamVector = new stream_vector(); + m_streamVector->reserve(PVR_STREAM_MAX_STREAMS); + } + + virtual ~XbmcStreamProperties(void) + { + delete m_streamVector; + } + + /** + * Resets the streams + */ + void Clear(void) + { + m_streamVector->clear(); + m_streamIndex.clear(); + } + + /** + * Returns the index of the stream with the specified physical ID, or -1 if + * there no stream is found. This method is called very often which is why + * we keep a separate map for this. + * @param iPhysicalId + * @return + */ + int GetStreamId(unsigned int iPhysicalId) const + { + std::map::const_iterator it = m_streamIndex.find(iPhysicalId); + if (it != m_streamIndex.end()) + return it->second; + + return -1; + } + + /** + * Returns the stream with the specified physical ID, or null if no such + * stream exists + * @param iPhysicalId + * @return + */ + XbmcPvrStream* GetStreamById(unsigned int iPhysicalId) const + { + int position = GetStreamId(iPhysicalId); + return position != -1 ? &m_streamVector->at(position) : NULL; + } + + /** + * Populates the specified stream with the stream having the specified + * physical ID. If the stream is not found only target stream's physical ID + * will be populated. + * @param iPhysicalId + * @param stream + */ + void GetStreamData(unsigned int iPhysicalId, XbmcPvrStream* stream) + { + XbmcPvrStream *foundStream = GetStreamById(iPhysicalId); + if (foundStream) + stream = foundStream; + else + { + stream->iIdentifier = -1; + stream->iPhysicalId = iPhysicalId; + } + } + + /** + * Populates props with the current streams and returns whether there are + * any streams at the moment or not. + * @param props + * @return + */ + bool GetProperties(PVR_STREAM_PROPERTIES* props) + { + unsigned int i = 0; + for (stream_vector::const_iterator it = m_streamVector->begin(); + it != m_streamVector->end(); ++it, ++i) + { + memcpy(&props->stream[i], &(*it), sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + } + + props->iStreamCount = m_streamVector->size(); + return (props->iStreamCount > 0); + } + + /** + * Merges new streams into the current list of streams. Identical streams + * will retain their respective indexes and new streams will replace unused + * indexes or be appended. + * @param newStreams + */ + void UpdateStreams(stream_vector &newStreams) + { + // sort the new streams + std::sort(newStreams.begin(), newStreams.end()); + + // ensure we never have more than PVR_STREAMS_MAX_STREAMS streams + if (newStreams.size() > PVR_STREAM_MAX_STREAMS) + { + while (newStreams.size() > PVR_STREAM_MAX_STREAMS) + newStreams.pop_back(); + + XBMC->Log(LOG_ERROR, "%s - max amount of streams reached", __FUNCTION__); + } + + stream_vector::iterator newStreamPosition; + for (stream_vector::iterator it = m_streamVector->begin(); it != m_streamVector->end(); ++it) + { + newStreamPosition = std::find(newStreams.begin(), newStreams.end(), *it); + + // if the current stream no longer exists we clear it, otherwise we + // copy it and remove it from newStreams + if (newStreamPosition == newStreams.end()) + it->Clear(); + else + { + *it = *newStreamPosition; + newStreams.erase(newStreamPosition); + } + } + + // replace cleared streams with new streams + for (stream_vector::iterator it = m_streamVector->begin(); + it != m_streamVector->end() && !newStreams.empty(); ++it) + { + if (it->IsCleared()) + { + *it = newStreams.front(); + newStreams.erase(newStreams.begin()); + } + } + + // append any remaining new streams + m_streamVector->insert(m_streamVector->end(), newStreams.begin(), newStreams.end()); + + // remove trailing cleared streams + while (m_streamVector->back().IsCleared()) + m_streamVector->pop_back(); + + // update the index + UpdateIndex(); + } + + private: + stream_vector *m_streamVector; + std::map m_streamIndex; + + /** + * Updates the stream index + */ + void UpdateIndex() + { + m_streamIndex.clear(); + + int i = 0; + for (stream_vector::const_iterator it = m_streamVector->begin(); it != m_streamVector->end(); ++it, ++i) + m_streamIndex[it->iPhysicalId] = i; + } + }; +} diff --git a/xbmc/addons/include/xbmc_vis_dll.h b/xbmc/addons/include/xbmc_vis_dll.h new file mode 100644 index 0000000..c65f844 --- /dev/null +++ b/xbmc/addons/include/xbmc_vis_dll.h @@ -0,0 +1,55 @@ +#ifndef __XBMC_VIS_H__ +#define __XBMC_VIS_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "xbmc_addon_dll.h" +#include "xbmc_vis_types.h" + +extern "C" +{ + // Functions that your visualisation must implement + void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); + void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); + void Render(); + bool OnAction(long action, const void *param); + void GetInfo(VIS_INFO* pInfo); + unsigned int GetPresets(char ***presets); + unsigned GetPreset(); + unsigned int GetSubModules(char ***presets); + bool IsLocked(); + + // function to export the above structure to XBMC + void __declspec(dllexport) get_addon(struct Visualisation* pVisz) + { + pVisz->Start = Start; + pVisz->AudioData = AudioData; + pVisz->Render = Render; + pVisz->OnAction = OnAction; + pVisz->GetInfo = GetInfo; + pVisz->GetPresets = GetPresets; + pVisz->GetPreset = GetPreset; + pVisz->GetSubModules = GetSubModules; + pVisz->IsLocked = IsLocked; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_vis_types.h b/xbmc/addons/include/xbmc_vis_types.h new file mode 100644 index 0000000..e6b0ccd --- /dev/null +++ b/xbmc/addons/include/xbmc_vis_types.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +/* + Common data structures shared between XBMC and XBMC's visualisations + */ + +#ifndef __VISUALISATION_TYPES_H__ +#define __VISUALISATION_TYPES_H__ +#include + +extern "C" +{ + struct VIS_INFO + { + int bWantsFreq; + int iSyncDelay; + }; + + struct VIS_PROPS + { + void *device; + int x; + int y; + int width; + int height; + float pixelRatio; + const char *name; + const char *presets; + const char *profile; + const char *submodule; + }; + + enum VIS_ACTION + { + VIS_ACTION_NONE = 0, + VIS_ACTION_NEXT_PRESET, + VIS_ACTION_PREV_PRESET, + VIS_ACTION_LOAD_PRESET, + VIS_ACTION_RANDOM_PRESET, + VIS_ACTION_LOCK_PRESET, + VIS_ACTION_RATE_PRESET_PLUS, + VIS_ACTION_RATE_PRESET_MINUS, + VIS_ACTION_UPDATE_ALBUMART, + VIS_ACTION_UPDATE_TRACK + }; + + class VisTrack + { + public: + VisTrack() + { + title = artist = album = albumArtist = NULL; + genre = comment = lyrics = reserved1 = reserved2 = NULL; + trackNumber = discNumber = duration = year = 0; + rating = 0; + reserved3 = reserved4 = 0; + } + + const char *title; + const char *artist; + const char *album; + const char *albumArtist; + const char *genre; + const char *comment; + const char *lyrics; + const char *reserved1; + const char *reserved2; + + int trackNumber; + int discNumber; + int duration; + int year; + char rating; + int reserved3; + int reserved4; + }; + + struct Visualisation + { + void (__cdecl* Start)(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); + void (__cdecl* AudioData)(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); + void (__cdecl* Render) (); + void (__cdecl* GetInfo)(VIS_INFO *info); + bool (__cdecl* OnAction)(long flags, const void *param); + int (__cdecl* HasPresets)(); + unsigned int (__cdecl *GetPresets)(char ***presets); + unsigned int (__cdecl *GetPreset)(); + unsigned int (__cdecl *GetSubModules)(char ***modules); + bool (__cdecl* IsLocked)(); + }; +} + +#endif //__VISUALISATION_TYPES_H__ diff --git a/xbmc/addons/test/Makefile b/xbmc/addons/test/Makefile new file mode 100644 index 0000000..bf74bd6 --- /dev/null +++ b/xbmc/addons/test/Makefile @@ -0,0 +1,9 @@ +SRCS= \ + TestAddonVersion.cpp + +LIB=addonsTest.a + +INCLUDES += -I../../../lib/gtest/include + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/addons/test/TestAddonVersion.cpp b/xbmc/addons/test/TestAddonVersion.cpp new file mode 100644 index 0000000..41e71a4 --- /dev/null +++ b/xbmc/addons/test/TestAddonVersion.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "addons/AddonVersion.h" + +#include "gtest/gtest.h" + +using namespace ADDON; + +class TestAddonVersion : public testing::Test +{ +public: + TestAddonVersion() + : v1_0("1.0"), + v1_00("1.00"), + v1_0_0("1.0.0"), + v1_1("1.1"), + v1_01("1.01"), + v1_0_1("1.0.1"), + e1_v1_0_0("1:1.0.0"), + e1_v1_0_1("1:1.0.1"), + e2_v1_0_0("2:1.0.0"), + e1_v1_0_0_r1("1:1.0.0-1"), + e1_v1_0_1_r1("1:1.0.1-1"), + e1_v1_0_0_r2("1:1.0.0-2"), + v1_0_0_beta("1.0.0~beta"), + v1_0_0_alpha("1.0.0~alpha"), + v1_0_0_alpha2("1.0.0~alpha2"), + v1_0_0_alpha3("1.0.0~alpha3"), + v1_0_0_alpha10("1.0.0~alpha10") + { + } + + AddonVersion v1_0; + AddonVersion v1_00; + AddonVersion v1_0_0; + AddonVersion v1_1; + AddonVersion v1_01; + AddonVersion v1_0_1; + AddonVersion e1_v1_0_0; + AddonVersion e1_v1_0_1; + AddonVersion e2_v1_0_0; + AddonVersion e1_v1_0_0_r1; + AddonVersion e1_v1_0_1_r1; + AddonVersion e1_v1_0_0_r2; + AddonVersion v1_0_0_beta; + AddonVersion v1_0_0_alpha; + AddonVersion v1_0_0_alpha2; + AddonVersion v1_0_0_alpha3; + AddonVersion v1_0_0_alpha10; +}; + +TEST_F(TestAddonVersion, Constructor) +{ + EXPECT_EQ(v1_0.Upstream(), "1.0"); + EXPECT_EQ(v1_0.Epoch(), 0); + EXPECT_TRUE(v1_0.Revision().empty()); + + EXPECT_EQ(v1_00.Upstream(), "1.00"); + EXPECT_EQ(v1_00.Epoch(), 0); + EXPECT_TRUE(v1_00.Revision().empty()); + + EXPECT_EQ(v1_0_0.Upstream(), "1.0.0"); + EXPECT_EQ(v1_0_0.Epoch(), 0); + EXPECT_TRUE(v1_0_0.Revision().empty()); + + EXPECT_EQ(v1_1.Upstream(), "1.1"); + EXPECT_EQ(v1_1.Epoch(), 0); + EXPECT_TRUE(v1_1.Revision().empty()); + + EXPECT_EQ(v1_01.Upstream(), "1.01"); + EXPECT_EQ(v1_01.Epoch(), 0); + EXPECT_TRUE(v1_01.Revision().empty()); + + EXPECT_EQ(v1_0_1.Upstream(), "1.0.1"); + EXPECT_EQ(v1_0_1.Epoch(), 0); + EXPECT_TRUE(v1_0_1.Revision().empty()); + + EXPECT_EQ(e1_v1_0_0.Upstream(), "1.0.0"); + EXPECT_EQ(e1_v1_0_0.Epoch(), 1); + EXPECT_TRUE(e1_v1_0_0.Revision().empty()); + + EXPECT_EQ(e1_v1_0_1.Upstream(), "1.0.1"); + EXPECT_EQ(e1_v1_0_1.Epoch(), 1); + EXPECT_TRUE(e1_v1_0_1.Revision().empty()); + + EXPECT_EQ(e2_v1_0_0.Upstream(), "1.0.0"); + EXPECT_EQ(e2_v1_0_0.Epoch(), 2); + EXPECT_TRUE(e2_v1_0_0.Revision().empty()); + + EXPECT_EQ(e1_v1_0_0_r1.Upstream(), "1.0.0"); + EXPECT_EQ(e1_v1_0_0_r1.Epoch(), 1); + EXPECT_EQ(e1_v1_0_0_r1.Revision(), "1"); + + EXPECT_EQ(e1_v1_0_1_r1.Upstream(), "1.0.1"); + EXPECT_EQ(e1_v1_0_1_r1.Epoch(), 1); + EXPECT_EQ(e1_v1_0_1_r1.Revision(), "1"); + + EXPECT_EQ(e1_v1_0_0_r2.Upstream(), "1.0.0"); + EXPECT_EQ(e1_v1_0_0_r2.Epoch(), 1); + EXPECT_EQ(e1_v1_0_0_r2.Revision(), "2"); + + EXPECT_EQ(v1_0_0_beta.Upstream(), "1.0.0~beta"); + EXPECT_EQ(v1_0_0_beta.Epoch(), 0); + EXPECT_TRUE(v1_0_0_beta.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha.Upstream(), "1.0.0~alpha"); + EXPECT_EQ(v1_0_0_alpha.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha2.Upstream(), "1.0.0~alpha2"); + EXPECT_EQ(v1_0_0_alpha2.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha2.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha3.Upstream(), "1.0.0~alpha3"); + EXPECT_EQ(v1_0_0_alpha3.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha3.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha10.Upstream(), "1.0.0~alpha10"); + EXPECT_EQ(v1_0_0_alpha10.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha10.Revision().empty()); +} + +TEST_F(TestAddonVersion, asString) +{ + EXPECT_EQ(v1_0.asString(), "1.0"); + EXPECT_EQ(v1_00.asString(), "1.00"); + EXPECT_EQ(v1_0_0.asString(), "1.0.0"); + EXPECT_EQ(v1_1.asString(), "1.1"); + EXPECT_EQ(v1_01.asString(), "1.01"); + EXPECT_EQ(v1_0_1.asString(), "1.0.1"); + EXPECT_EQ(e1_v1_0_0.asString(), "1:1.0.0"); + EXPECT_EQ(e1_v1_0_1.asString(), "1:1.0.1"); + EXPECT_EQ(e2_v1_0_0.asString(), "2:1.0.0"); + EXPECT_EQ(e1_v1_0_0_r1.asString(), "1:1.0.0-1"); + EXPECT_EQ(e1_v1_0_1_r1.asString(), "1:1.0.1-1"); + EXPECT_EQ(e1_v1_0_0_r2.asString(), "1:1.0.0-2"); + EXPECT_EQ(v1_0_0_beta.asString(), "1.0.0~beta"); + EXPECT_EQ(v1_0_0_alpha.asString(), "1.0.0~alpha"); + EXPECT_EQ(v1_0_0_alpha2.asString(), "1.0.0~alpha2"); + EXPECT_EQ(v1_0_0_alpha3.asString(), "1.0.0~alpha3"); + EXPECT_EQ(v1_0_0_alpha10.asString(), "1.0.0~alpha10"); +} + +TEST_F(TestAddonVersion, Equals) +{ + EXPECT_EQ(v1_0, AddonVersion("1.0")); + EXPECT_EQ(v1_00, AddonVersion("1.00")); + EXPECT_EQ(v1_0_0, AddonVersion("1.0.0")); + EXPECT_EQ(v1_1, AddonVersion("1.1")); + EXPECT_EQ(v1_01, AddonVersion("1.01")); + EXPECT_EQ(v1_0_1, AddonVersion("1.0.1")); + EXPECT_EQ(e1_v1_0_0, AddonVersion("1:1.0.0")); + EXPECT_EQ(e1_v1_0_1, AddonVersion("1:1.0.1")); + EXPECT_EQ(e2_v1_0_0, AddonVersion("2:1.0.0")); + EXPECT_EQ(e1_v1_0_0_r1, AddonVersion("1:1.0.0-1")); + EXPECT_EQ(e1_v1_0_1_r1, AddonVersion("1:1.0.1-1")); + EXPECT_EQ(e1_v1_0_0_r2, AddonVersion("1:1.0.0-2")); + EXPECT_EQ(v1_0_0_beta, AddonVersion("1.0.0~beta")); + EXPECT_EQ(v1_0_0_alpha, AddonVersion("1.0.0~alpha")); + EXPECT_EQ(v1_0_0_alpha2, AddonVersion("1.0.0~alpha2")); + EXPECT_EQ(v1_0_0_alpha3, AddonVersion("1.0.0~alpha3")); + EXPECT_EQ(v1_0_0_alpha10, AddonVersion("1.0.0~alpha10")); +} + +TEST_F(TestAddonVersion, Equivalent) +{ + EXPECT_FALSE(v1_0 != v1_00); + EXPECT_FALSE(v1_0 < v1_00); + EXPECT_FALSE(v1_0 > v1_00); + EXPECT_TRUE(v1_0 == v1_00); + + EXPECT_FALSE(v1_01 != v1_1); + EXPECT_FALSE(v1_01 < v1_1); + EXPECT_FALSE(v1_01 > v1_1); + EXPECT_TRUE(v1_01 == v1_1); +} + +TEST_F(TestAddonVersion, LessThan) +{ + EXPECT_LT(v1_0, v1_0_0); + EXPECT_LT(v1_0, v1_1); + EXPECT_LT(v1_0, v1_01); + EXPECT_LT(v1_0, v1_0_1); + + EXPECT_LT(v1_00, v1_0_0); + EXPECT_LT(v1_00, v1_1); + EXPECT_LT(v1_00, v1_01); + EXPECT_LT(v1_00, v1_0_1); + + EXPECT_LT(v1_0_0, v1_1); + EXPECT_LT(v1_0_0, v1_01); + EXPECT_LT(v1_0_0, v1_0_1); + + EXPECT_LT(v1_0_1, v1_01); + EXPECT_LT(v1_0_1, v1_1); + + // epochs + EXPECT_LT(v1_0_0, e1_v1_0_0); + EXPECT_LT(v1_0_0, e1_v1_0_1); + EXPECT_LT(v1_0_0, e2_v1_0_0); + EXPECT_LT(v1_0_1, e1_v1_0_1); + EXPECT_LT(v1_0_1, e2_v1_0_0); + + EXPECT_LT(e1_v1_0_0, e1_v1_0_1); + EXPECT_LT(e1_v1_0_0, e2_v1_0_0); + EXPECT_LT(e1_v1_0_1, e2_v1_0_0); + + // revisions + EXPECT_LT(e1_v1_0_0, e1_v1_0_0_r1); + EXPECT_LT(e1_v1_0_0, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_0, e1_v1_0_0_r2); + EXPECT_LT(e1_v1_0_1, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_0_r1, e1_v1_0_1); + EXPECT_LT(e1_v1_0_0_r1, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_0_r1, e1_v1_0_0_r2); + EXPECT_LT(e1_v1_0_0_r2, e1_v1_0_1); + EXPECT_LT(e1_v1_0_0_r2, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_1_r1, e2_v1_0_0); + + // alpha, beta + EXPECT_LT(v1_0_0_beta, v1_0_0); + EXPECT_LT(v1_0_0_alpha, v1_0_0); + EXPECT_LT(v1_0_0_alpha, v1_0_0_beta); + EXPECT_LT(v1_0_0_alpha, v1_0_0_alpha2); + EXPECT_LT(v1_0_0_alpha, v1_0_0_alpha3); + EXPECT_LT(v1_0_0_alpha, v1_0_0_alpha10); + EXPECT_LT(v1_0_0_alpha2, v1_0_0); + EXPECT_LT(v1_0_0_alpha2, v1_0_0_beta); + EXPECT_LT(v1_0_0_alpha2, v1_0_0_alpha3); + EXPECT_LT(v1_0_0_alpha2, v1_0_0_alpha10); + EXPECT_LT(v1_0_0_alpha3, v1_0_0); + EXPECT_LT(v1_0_0_alpha3, v1_0_0_beta); + EXPECT_LT(v1_0_0_alpha3, v1_0_0_alpha10); + EXPECT_LT(v1_0_0_alpha10, v1_0_0); + EXPECT_LT(v1_0_0_alpha10, v1_0_0_beta); +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp new file mode 100644 index 0000000..2f833c5 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include "DVDCodecs/DVDCodecs.h" + +void CDemuxStreamTeletext::GetStreamInfo(std::string& strInfo) +{ + strInfo = "Teletext Data Stream"; +} + +void CDemuxStreamAudio::GetStreamType(std::string& strInfo) +{ + char sInfo[64]; + + if (codec == AV_CODEC_ID_AC3) strcpy(sInfo, "AC3 "); + else if (codec == AV_CODEC_ID_DTS) + { +#ifdef FF_PROFILE_DTS_HD_MA + if (profile == FF_PROFILE_DTS_HD_MA) + strcpy(sInfo, "DTS-HD MA "); + else if (profile == FF_PROFILE_DTS_HD_HRA) + strcpy(sInfo, "DTS-HD HRA "); + else +#endif + strcpy(sInfo, "DTS "); + } + else if (codec == AV_CODEC_ID_MP2) strcpy(sInfo, "MP2 "); + else if (codec == AV_CODEC_ID_TRUEHD) strcpy(sInfo, "Dolby TrueHD "); + else strcpy(sInfo, ""); + + if (iChannels == 1) strcat(sInfo, "Mono"); + else if (iChannels == 2) strcat(sInfo, "Stereo"); + else if (iChannels == 6) strcat(sInfo, "5.1"); + else if (iChannels == 8) strcat(sInfo, "7.1"); + else if (iChannels != 0) + { + char temp[32]; + sprintf(temp, " %d%s", iChannels, "-chs"); + strcat(sInfo, temp); + } + strInfo = sInfo; +} + +int CDVDDemux::GetNrOfAudioStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_AUDIO) iCounter++; + } + + return iCounter; +} + +int CDVDDemux::GetNrOfVideoStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_VIDEO) iCounter++; + } + + return iCounter; +} + +int CDVDDemux::GetNrOfSubtitleStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_SUBTITLE) iCounter++; + } + + return iCounter; +} + +int CDVDDemux::GetNrOfTeletextStreams() +{ + int iCounter = 0; + + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + if (pStream->type == STREAM_TELETEXT) iCounter++; + } + + return iCounter; +} + +CDemuxStreamAudio* CDVDDemux::GetStreamFromAudioId(int iAudioIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_AUDIO) counter++; + if (iAudioIndex == counter) + return (CDemuxStreamAudio*)pStream; + } + return NULL; +} + +CDemuxStreamVideo* CDVDDemux::GetStreamFromVideoId(int iVideoIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_VIDEO) counter++; + if (iVideoIndex == counter) + return (CDemuxStreamVideo*)pStream; + } + return NULL; +} + +CDemuxStreamSubtitle* CDVDDemux::GetStreamFromSubtitleId(int iSubtitleIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_SUBTITLE) counter++; + if (iSubtitleIndex == counter) + return (CDemuxStreamSubtitle*)pStream; + } + return NULL; +} + +CDemuxStreamTeletext* CDVDDemux::GetStreamFromTeletextId(int iTeletextIndex) +{ + int counter = -1; + for (int i = 0; i < GetNrOfStreams(); i++) + { + CDemuxStream* pStream = GetStream(i); + + if (pStream->type == STREAM_TELETEXT) counter++; + if (iTeletextIndex == counter) + return (CDemuxStreamTeletext*)pStream; + } + return NULL; +} + +void CDemuxStream::GetStreamName( std::string& strInfo ) +{ + strInfo = ""; +} + +AVDiscard CDemuxStream::GetDiscard() +{ + return AVDISCARD_NONE; +} + +void CDemuxStream::SetDiscard(AVDiscard discard) +{ + return; +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h new file mode 100644 index 0000000..fca164d --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h @@ -0,0 +1,359 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include "system.h" +#include "DVDDemuxPacket.h" + +class CDVDInputStream; + +#ifndef __GNUC__ +#pragma warning(push) +#pragma warning(disable:4244) +#endif + +#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS) + #include "config.h" +#endif + +extern "C" { +#include "libavcodec/avcodec.h" +} + +#ifndef __GNUC__ +#pragma warning(pop) +#endif + +enum AVDiscard; + +enum StreamType +{ + STREAM_NONE = 0,// if unknown + STREAM_AUDIO, // audio stream + STREAM_VIDEO, // video stream + STREAM_DATA, // data stream + STREAM_SUBTITLE,// subtitle stream + STREAM_TELETEXT // Teletext data stream +}; + +enum StreamSource { + STREAM_SOURCE_NONE = 0x000, + STREAM_SOURCE_DEMUX = 0x100, + STREAM_SOURCE_NAV = 0x200, + STREAM_SOURCE_DEMUX_SUB = 0x300, + STREAM_SOURCE_TEXT = 0x400, + STREAM_SOURCE_VIDEOMUX = 0x500 +}; + +#define STREAM_SOURCE_MASK(a) ((a) & 0xf00) + +/* + * CDemuxStream + * Base class for all demuxer streams + */ +class CDemuxStream +{ +public: + CDemuxStream() + { + iId = 0; + iPhysicalId = 0; + codec = (AVCodecID)0; // AV_CODEC_ID_NONE + codec_fourcc = 0; + profile = FF_PROFILE_UNKNOWN; + level = FF_LEVEL_UNKNOWN; + type = STREAM_NONE; + source = STREAM_SOURCE_NONE; + iDuration = 0; + pPrivate = NULL; + ExtraData = NULL; + ExtraSize = 0; + memset(language, 0, sizeof(language)); + disabled = false; + changes = 0; + flags = FLAG_NONE; + orig_type = 0; + } + + virtual ~CDemuxStream() + { + delete [] ExtraData; + } + + virtual void GetStreamInfo(std::string& strInfo) + { + strInfo = ""; + } + + virtual void GetStreamName(std::string& strInfo); + + virtual void SetDiscard(AVDiscard discard); + virtual AVDiscard GetDiscard(); + + int iId; // most of the time starting from 0 + int iPhysicalId; // id + AVCodecID codec; + unsigned int codec_fourcc; // if available + int profile; // encoder profile of the stream reported by the decoder. used to qualify hw decoders. + int level; // encoder level of the stream reported by the decoder. used to qualify hw decoders. + StreamType type; + int source; + + int iDuration; // in mseconds + void* pPrivate; // private pointer for the demuxer + uint8_t* ExtraData; // extra data for codec to use + unsigned int ExtraSize; // size of extra data + + char language[4]; // ISO 639 3-letter language code (empty string if undefined) + bool disabled; // set when stream is disabled. (when no decoder exists) + + int changes; // increment on change which player may need to know about + + int orig_type; // type of original source + + enum EFlags + { FLAG_NONE = 0x0000 + , FLAG_DEFAULT = 0x0001 + , FLAG_DUB = 0x0002 + , FLAG_ORIGINAL = 0x0004 + , FLAG_COMMENT = 0x0008 + , FLAG_LYRICS = 0x0010 + , FLAG_KARAOKE = 0x0020 + , FLAG_FORCED = 0x0040 + , FLAG_HEARING_IMPAIRED = 0x0080 + , FLAG_VISUAL_IMPAIRED = 0x0100 + } flags; +}; + +class CDemuxStreamVideo : public CDemuxStream +{ +public: + CDemuxStreamVideo() : CDemuxStream() + { + iFpsScale = 0; + iFpsRate = 0; + irFpsScale = 0; + irFpsRate = 0; + iHeight = 0; + iWidth = 0; + fAspect = 0.0; + bVFR = false; + bPTSInvalid = false; + bForcedAspect = false; + type = STREAM_VIDEO; + iOrientation = 0; + iBitsPerPixel = 0; + } + + virtual ~CDemuxStreamVideo() {} + int iFpsScale; // scale of 1000 and a rate of 29970 will result in 29.97 fps + int iFpsRate; + int irFpsScale; + int irFpsRate; + int iHeight; // height of the stream reported by the demuxer + int iWidth; // width of the stream reported by the demuxer + float fAspect; // display aspect of stream + bool bVFR; // variable framerate + bool bPTSInvalid; // pts cannot be trusted (avi's). + bool bForcedAspect; // aspect is forced from container + int iOrientation; // orientation of the video in degress counter clockwise + int iBitsPerPixel; + std::string stereo_mode; // expected stereo mode +}; + +class CDemuxStreamAudio : public CDemuxStream +{ +public: + CDemuxStreamAudio() : CDemuxStream() + { + iChannels = 0; + iSampleRate = 0; + iBlockAlign = 0; + iBitRate = 0; + iBitsPerSample = 0; + type = STREAM_AUDIO; + } + + virtual ~CDemuxStreamAudio() {} + + void GetStreamType(std::string& strInfo); + + int iChannels; + int iSampleRate; + int iBlockAlign; + int iBitRate; + int iBitsPerSample; +}; + +class CDemuxStreamSubtitle : public CDemuxStream +{ +public: + CDemuxStreamSubtitle() : CDemuxStream() + { + type = STREAM_SUBTITLE; + } +}; + +class CDemuxStreamTeletext : public CDemuxStream +{ +public: + CDemuxStreamTeletext() : CDemuxStream() + { + type = STREAM_TELETEXT; + } + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDVDDemux +{ +public: + + CDVDDemux() {} + virtual ~CDVDDemux() {} + + + /* + * Reset the entire demuxer (same result as closing and opening it) + */ + virtual void Reset() = 0; + + /* + * Aborts any internal reading that might be stalling main thread + * NOTICE - this can be called from another thread + */ + virtual void Abort() = 0; + + /* + * Flush the demuxer, if any data is kept in buffers, this should be freed now + */ + virtual void Flush() = 0; + + /* + * Read a packet, returns NULL on error + * + */ + virtual DemuxPacket* Read() = 0; + + /* + * Seek, time in msec calculated from stream start + */ + virtual bool SeekTime(int time, bool backwords = false, double* startpts = NULL) = 0; + + /* + * Seek to a specified chapter. + * startpts can be updated to the point where display should start + */ + virtual bool SeekChapter(int chapter, double* startpts = NULL) { return false; } + + /* + * Get the number of chapters available + */ + virtual int GetChapterCount() { return 0; } + + /* + * Get current chapter + */ + virtual int GetChapter() { return 0; } + + /* + * Get the name of a chapter + * \param strChapterName[out] Name of chapter + * \param chapterIdx -1 for current chapter, else a chapter index + */ + virtual void GetChapterName(std::string& strChapterName, int chapterIdx=-1) {} + + /* + * Get the position of a chapter + * \param chapterIdx -1 for current chapter, else a chapter index + */ + virtual int64_t GetChapterPos(int chapterIdx=-1) { return 0; } + + /* + * Set the playspeed, if demuxer can handle different + * speeds of playback + */ + virtual void SetSpeed(int iSpeed) = 0; + + /* + * returns the total time in msec + */ + virtual int GetStreamLength() = 0; + + /* + * returns the stream or NULL on error, starting from 0 + */ + virtual CDemuxStream* GetStream(int iStreamId) = 0; + + /* + * return nr of streams, 0 if none + */ + virtual int GetNrOfStreams() = 0; + + /* + * returns opened filename + */ + virtual std::string GetFileName() = 0; + /* + * return nr of audio streams, 0 if none + */ + int GetNrOfAudioStreams(); + + /* + * return nr of video streams, 0 if none + */ + int GetNrOfVideoStreams(); + + /* + * return nr of subtitle streams, 0 if none + */ + int GetNrOfSubtitleStreams(); + + /* + * return nr of teletext streams, 0 if none + */ + int GetNrOfTeletextStreams(); + + /* + * return the audio stream, or NULL if it does not exist + */ + CDemuxStreamAudio* GetStreamFromAudioId(int iAudioIndex); + + /* + * return the video stream, or NULL if it does not exist + */ + CDemuxStreamVideo* GetStreamFromVideoId(int iVideoIndex); + + /* + * return the subtitle stream, or NULL if it does not exist + */ + CDemuxStreamSubtitle* GetStreamFromSubtitleId(int iSubtitleIndex); + + /* + * return the teletext stream, or NULL if it does not exist + */ + CDemuxStreamTeletext* GetStreamFromTeletextId(int iTeletextIndex); + + /* + * return a user-presentable codec name of the given stream + */ + virtual void GetStreamCodecName(int iStreamId, std::string &strName) {}; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp new file mode 100644 index 0000000..9786983 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxBXA.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "../DVDClock.h" + +// AirTunes audio Demuxer. + +using namespace std; + +class CDemuxStreamAudioBXA + : public CDemuxStreamAudio +{ + CDVDDemuxBXA *m_parent; + string m_codec; +public: + CDemuxStreamAudioBXA(CDVDDemuxBXA *parent, const string& codec) + : m_parent(parent) + , m_codec(codec) + + {} + void GetStreamInfo(string& strInfo) + { + strInfo = StringUtils::Format("%s", m_codec.c_str()); + } +}; + +CDVDDemuxBXA::CDVDDemuxBXA() : CDVDDemux() +{ + m_pInput = NULL; + m_stream = NULL; + m_bytes = 0; + memset(&m_header, 0x0, sizeof(Demux_BXA_FmtHeader)); +} + +CDVDDemuxBXA::~CDVDDemuxBXA() +{ + Dispose(); +} + +bool CDVDDemuxBXA::Open(CDVDInputStream* pInput) +{ + Abort(); + + Dispose(); + + if(!pInput || !pInput->IsStreamType(DVDSTREAM_TYPE_FILE)) + return false; + + if(pInput->Read((uint8_t *)&m_header, sizeof(Demux_BXA_FmtHeader)) < 1) + return false; + + // file valid? + if (strncmp(m_header.fourcc, "BXA ", 4) != 0 || m_header.type != BXA_PACKET_TYPE_FMT_DEMUX) + { + pInput->Seek(0, SEEK_SET); + return false; + } + + m_pInput = pInput; + + m_stream = new CDemuxStreamAudioBXA(this, "BXA"); + + if(!m_stream) + return false; + + m_stream->iSampleRate = m_header.sampleRate; + m_stream->iBitsPerSample = m_header.bitsPerSample; + m_stream->iBitRate = m_header.sampleRate * m_header.channels * m_header.bitsPerSample; + m_stream->iChannels = m_header.channels; + m_stream->type = STREAM_AUDIO; + m_stream->codec = AV_CODEC_ID_PCM_S16LE; + + return true; +} + +void CDVDDemuxBXA::Dispose() +{ + delete m_stream; + m_stream = NULL; + + m_pInput = NULL; + m_bytes = 0; + + memset(&m_header, 0x0, sizeof(Demux_BXA_FmtHeader)); +} + +void CDVDDemuxBXA::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxBXA::Abort() +{ + if(m_pInput) + return m_pInput->Abort(); +} + +void CDVDDemuxBXA::Flush() +{ +} + +#define BXA_READ_SIZE 4096 +DemuxPacket* CDVDDemuxBXA::Read() +{ + if(!m_pInput) + return NULL; + + DemuxPacket* pPacket = CDVDDemuxUtils::AllocateDemuxPacket(BXA_READ_SIZE); + + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + pPacket->iSize = m_pInput->Read(pPacket->pData, BXA_READ_SIZE); + pPacket->iStreamId = 0; + + if(pPacket->iSize < 1) + { + delete pPacket; + pPacket = NULL; + } + else + { + int n = (m_header.channels * m_header.bitsPerSample * m_header.sampleRate)>>3; + if (n > 0) + { + m_bytes += pPacket->iSize; + pPacket->dts = (double)m_bytes * DVD_TIME_BASE / n; + pPacket->pts = pPacket->dts; + } + else + { + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + } + } + + return pPacket; +} + +CDemuxStream* CDVDDemuxBXA::GetStream(int iStreamId) +{ + if(iStreamId != 0) + return NULL; + + return m_stream; +} + +int CDVDDemuxBXA::GetNrOfStreams() +{ + return (m_stream == NULL ? 0 : 1); +} + +std::string CDVDDemuxBXA::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxBXA::GetStreamCodecName(int iStreamId, std::string &strName) +{ + if (m_stream && iStreamId == 0) + strName = "BXA"; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h new file mode 100644 index 0000000..bdb65b4 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h @@ -0,0 +1,85 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" + +#ifdef TARGET_WINDOWS +#define __attribute__(dummy_val) +#else +#include +#endif + +#ifdef TARGET_WINDOWS +#pragma pack(push) +#pragma pack(1) +#endif + +typedef struct +{ + char fourcc[4]; + uint32_t type; + uint32_t channels; + uint32_t sampleRate; + uint32_t bitsPerSample; + uint64_t durationMs; +} __attribute__((__packed__)) Demux_BXA_FmtHeader; + +#ifdef TARGET_WINDOWS +#pragma pack(pop) +#endif + +#include + +#define BXA_PACKET_TYPE_FMT_DEMUX 1 + +class CDemuxStreamAudioBXA; + +class CDVDDemuxBXA : public CDVDDemux +{ +public: + + CDVDDemuxBXA(); + ~CDVDDemuxBXA(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; } + void SetSpeed(int iSpeed) {}; + int GetStreamLength() { return (int)m_header.durationMs; } + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + +protected: + friend class CDemuxStreamAudioBXA; + CDVDInputStream* m_pInput; + int64_t m_bytes; + + CDemuxStreamAudioBXA *m_stream; + + Demux_BXA_FmtHeader m_header; +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp new file mode 100644 index 0000000..24d56da --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxUtils.h" +#include "DVDClock.h" +#include "DVDDemuxCC.h" +#include "cores/dvdplayer/DVDCodecs/Overlay/contrib/cc_decoder708.h" +#include "utils/log.h" + +#include + +class CBitstream +{ +public: + CBitstream(uint8_t *data, int bits) + { + m_data = data; + m_offset = 0; + m_len = bits; + m_error = false; + } + unsigned int readBits(int num) + { + int r = 0; + while (num > 0) + { + if (m_offset >= m_len) + { + m_error = true; + return 0; + } + num--; + if (m_data[m_offset / 8] & (1 << (7 - (m_offset & 7)))) + r |= 1 << num; + m_offset++; + } + return r; + } + unsigned int readGolombUE(int maxbits = 32) + { + int lzb = -1; + int bits = 0; + for (int b = 0; !b; lzb++, bits++) + { + if (bits > maxbits) + return 0; + b = readBits(1); + } + return (1 << lzb) - 1 + readBits(lzb); + } + +private: + uint8_t *m_data; + int m_offset; + int m_len; + bool m_error; +}; + +class CCaptionBlock +{ +public: + CCaptionBlock(int size) + { + m_data = (uint8_t*)malloc(size); + m_size = size; + } + virtual ~CCaptionBlock() + { + free(m_data); + } + double m_pts; + uint8_t *m_data; + int m_size; +}; + +bool reorder_sort (CCaptionBlock *lhs, CCaptionBlock *rhs) +{ + return (lhs->m_pts > rhs->m_pts); +} + +CDVDDemuxCC::CDVDDemuxCC(AVCodecID codec) +{ + m_hasData = false; + m_curPts = 0; + m_ccDecoder = NULL; + m_codec = codec; +} + +CDVDDemuxCC::~CDVDDemuxCC() +{ + Dispose(); +} + +CDemuxStream* CDVDDemuxCC::GetStream(int iStreamId) +{ + return &m_streams[iStreamId]; +} + +int CDVDDemuxCC::GetNrOfStreams() +{ + return m_streams.size(); +} + +DemuxPacket* CDVDDemuxCC::Read(DemuxPacket *pSrcPacket) +{ + DemuxPacket *pPacket = NULL; + uint32_t startcode = 0xffffffff; + int picType = 0; + int p = 0; + int len; + + if (!pSrcPacket) + { + pPacket = Decode(); + return pPacket; + } + if (pSrcPacket->pts == DVD_NOPTS_VALUE) + { + return pPacket; + } + + while (!m_ccTempBuffer.empty()) + { + m_ccReorderBuffer.push_back(m_ccTempBuffer.back()); + m_ccTempBuffer.pop_back(); + } + + while ((len = pSrcPacket->iSize - p) > 3) + { + if ((startcode & 0xffffff00) == 0x00000100) + { + if (m_codec == AV_CODEC_ID_MPEG2VIDEO) + { + int scode = startcode & 0xFF; + if (scode == 0x00) + { + if (len > 4) + { + uint8_t *buf = pSrcPacket->pData + p; + picType = (buf[1] & 0x38) >> 3; + } + } + else if (scode == 0xb2) // user data + { + uint8_t *buf = pSrcPacket->pData + p; + if (len >= 6 && + buf[0] == 'G' && buf[1] == 'A' && buf[2] == '9' && buf[3] == '4' && + buf[4] == 3 && (buf[5] & 0x40)) + { + int cc_count = buf[5] & 0x1f; + if (cc_count > 0 && len >= 7 + cc_count * 3) + { + CCaptionBlock *cc = new CCaptionBlock(cc_count * 3); + memcpy(cc->m_data, buf + 7, cc_count * 3); + cc->m_pts = pSrcPacket->pts; + if (picType == 1 || picType == 2) + m_ccTempBuffer.push_back(cc); + else + m_ccReorderBuffer.push_back(cc); + } + } + else if (len >= 6 && + buf[0] == 'C' && buf[1] == 'C' && buf[2] == 1) + { + int oddidx = (buf[4] & 0x80) ? 0 : 1; + int cc_count = (buf[4] & 0x3e) >> 1; + int extrafield = buf[4] & 0x01; + if (extrafield) + cc_count++; + + if (cc_count > 0 && len >= 5 + cc_count * 3 * 2) + { + CCaptionBlock *cc = new CCaptionBlock(cc_count * 3); + uint8_t *src = buf + 5; + uint8_t *dst = cc->m_data; + + for (int i = 0; i < cc_count; i++) + { + for (int j = 0; j < 2; j++) + { + if (i == cc_count - 1 && extrafield && j == 1) + break; + + if ((oddidx == j) && (src[0] == 0xFF)) + { + dst[0] = 0x04; + dst[1] = src[1]; + dst[2] = src[2]; + dst += 3; + } + src += 3; + } + } + cc->m_pts = pSrcPacket->pts; + m_ccReorderBuffer.push_back(cc); + picType = 1; + } + } + } + } + else if (m_codec == AV_CODEC_ID_H264) + { + int scode = startcode & 0x9F; + // slice data comes after SEI + if (scode >= 1 && scode <= 5) + { + uint8_t *buf = pSrcPacket->pData + p; + CBitstream bs(buf, len * 8); + bs.readGolombUE(); + int sliceType = bs.readGolombUE(); + if (sliceType == 2 || sliceType == 7) // I slice + picType = 1; + else if (sliceType == 0 || sliceType == 5) // P slice + picType = 2; + if (picType == 0) + { + while (!m_ccTempBuffer.empty()) + { + m_ccReorderBuffer.push_back(m_ccTempBuffer.back()); + m_ccTempBuffer.pop_back(); + } + } + } + if (scode == 0x06) // SEI + { + uint8_t *buf = pSrcPacket->pData + p; + if (len >= 12 && + buf[3] == 0 && buf[4] == 49 && + buf[5] == 'G' && buf[6] == 'A' && buf[7] == '9' && buf[8] == '4' && buf[9] == 3) + { + uint8_t *userdata = buf + 10; + int cc_count = userdata[0] & 0x1f; + if (len >= cc_count * 3 + 10) + { + CCaptionBlock *cc = new CCaptionBlock(cc_count * 3); + memcpy(cc->m_data, userdata + 2, cc_count * 3); + cc->m_pts = pSrcPacket->pts; + m_ccTempBuffer.push_back(cc); + } + } + } + } + } + startcode = startcode << 8 | pSrcPacket->pData[p++]; + } + + if ((picType == 1 || picType == 2) && !m_ccReorderBuffer.empty()) + { + if (!m_ccDecoder) + { + if (!OpenDecoder()) + return NULL; + } + std::sort(m_ccReorderBuffer.begin(), m_ccReorderBuffer.end(), reorder_sort); + pPacket = Decode(); + } + return pPacket; +} + +void CDVDDemuxCC::Handler(int service, void *userdata) +{ + CDVDDemuxCC *ctx = (CDVDDemuxCC*)userdata; + + unsigned int idx; + + // switch back from 608 fallback if we got 708 + if (ctx->m_ccDecoder->m_seen608 && ctx->m_ccDecoder->m_seen708) + { + for (idx = 0; idx < ctx->m_streamdata.size(); idx++) + { + if (ctx->m_streamdata[idx].service == 0) + break; + } + if (idx < ctx->m_streamdata.size()) + { + ctx->m_streamdata.erase(ctx->m_streamdata.begin() + idx); + ctx->m_ccDecoder->m_seen608 = false; + } + if (service == 0) + return; + } + + for (idx = 0; idx < ctx->m_streamdata.size(); idx++) + { + if (ctx->m_streamdata[idx].service == service) + break; + } + if (idx >= ctx->m_streamdata.size()) + { + CDemuxStreamSubtitle stream; + strcpy(stream.language, "cc"); + stream.codec = AV_CODEC_ID_TEXT; + stream.iPhysicalId = service; + stream.iId = idx; + ctx->m_streams.push_back(stream); + + streamdata data; + data.streamIdx = idx; + data.service = service; + ctx->m_streamdata.push_back(data); + + if (service == 0) + ctx->m_ccDecoder->m_seen608 = true; + else + ctx->m_ccDecoder->m_seen708 = true; + } + + ctx->m_streamdata[idx].pts = ctx->m_curPts; + ctx->m_streamdata[idx].hasData = true; + ctx->m_hasData = true; +} + +bool CDVDDemuxCC::OpenDecoder() +{ + m_ccDecoder = new CDecoderCC708(); + m_ccDecoder->Init(Handler, this); + return true; +} + +void CDVDDemuxCC::Dispose() +{ + m_streams.clear(); + m_streamdata.clear(); + delete m_ccDecoder; + m_ccDecoder = NULL; + + while (!m_ccReorderBuffer.empty()) + { + delete m_ccReorderBuffer.back(); + m_ccReorderBuffer.pop_back(); + } + while (!m_ccTempBuffer.empty()) + { + delete m_ccTempBuffer.back(); + m_ccTempBuffer.pop_back(); + } +} + +DemuxPacket* CDVDDemuxCC::Decode() +{ + DemuxPacket *pPacket = NULL; + + while(!m_hasData && !m_ccReorderBuffer.empty()) + { + CCaptionBlock *cc = m_ccReorderBuffer.back(); + m_ccReorderBuffer.pop_back(); + m_curPts = cc->m_pts; + m_ccDecoder->Decode(cc->m_data, cc->m_size); + delete cc; + } + + if (m_hasData) + { + for (unsigned int i=0; im_cc608decoder->text; + len = m_ccDecoder->m_cc608decoder->textlen; + } + else + { + data = m_ccDecoder->m_cc708decoders[service].text; + len = m_ccDecoder->m_cc708decoders[service].textlen; + } + + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(len); + pPacket->iSize = len; + memcpy(pPacket->pData, data, pPacket->iSize); + + pPacket->iStreamId = i; + pPacket->pts = m_streamdata[i].pts; + pPacket->duration = 0; + m_streamdata[i].hasData = false; + break; + } + m_hasData = false; + } + } + return pPacket; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h new file mode 100644 index 0000000..ae78298 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCC.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once +#include "DVDDemux.h" +#include + +class CCaptionBlock; +class CDecoderCC708; + +class CDVDDemuxCC : public CDVDDemux +{ +public: + CDVDDemuxCC(AVCodecID codec); + virtual ~CDVDDemuxCC(); + + virtual void Reset() {}; + virtual void Abort() {}; + virtual void Flush() {}; + virtual DemuxPacket* Read() { return NULL; }; + virtual bool SeekTime(int time, bool backwords = false, double* startpts = NULL) {return true;}; + virtual void SetSpeed(int iSpeed) {}; + virtual int GetStreamLength() {return 0;}; + virtual CDemuxStream* GetStream(int iStreamId); + virtual int GetNrOfStreams(); + virtual std::string GetFileName() {return "";}; + + DemuxPacket* Read(DemuxPacket *packet); + static void Handler(int service, void *userdata); + +protected: + bool OpenDecoder(); + void Dispose(); + DemuxPacket* Decode(); + + struct streamdata + { + int streamIdx; + int service; + bool hasData ; + double pts; + }; + std::vector m_streamdata; + std::vector m_streams; + bool m_hasData; + double m_curPts; + std::vector m_ccReorderBuffer; + std::vector m_ccTempBuffer; + CDecoderCC708 *m_ccDecoder; + AVCodecID m_codec; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp new file mode 100644 index 0000000..72067da --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxCDDA.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "../DVDClock.h" + +// CDDA audio demuxer based on AirTunes audio Demuxer. + +using namespace std; + +class CDemuxStreamAudioCDDA + : public CDemuxStreamAudio +{ +public: + void GetStreamInfo(string& strInfo) + { + strInfo = "pcm"; + } +}; + +CDVDDemuxCDDA::CDVDDemuxCDDA() : CDVDDemux() +{ + m_pInput = NULL; + m_stream = NULL; + m_bytes = 0; +} + +CDVDDemuxCDDA::~CDVDDemuxCDDA() +{ + Dispose(); +} + +bool CDVDDemuxCDDA::Open(CDVDInputStream* pInput) +{ + Abort(); + + Dispose(); + + if(!pInput || !pInput->IsStreamType(DVDSTREAM_TYPE_FILE)) + return false; + + m_pInput = pInput; + + m_stream = new CDemuxStreamAudioCDDA(); + + if(!m_stream) + return false; + + m_stream->iSampleRate = 44100; + m_stream->iBitsPerSample = 16; + m_stream->iBitRate = 44100 * 2 * 16; + m_stream->iChannels = 2; + m_stream->type = STREAM_AUDIO; + m_stream->codec = AV_CODEC_ID_PCM_S16LE; + + return true; +} + +void CDVDDemuxCDDA::Dispose() +{ + delete m_stream; + m_stream = NULL; + + m_pInput = NULL; + m_bytes = 0; +} + +void CDVDDemuxCDDA::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxCDDA::Abort() +{ + if(m_pInput) + return m_pInput->Abort(); +} + +void CDVDDemuxCDDA::Flush() +{ +} + +#define CDDA_READ_SIZE 4096 +DemuxPacket* CDVDDemuxCDDA::Read() +{ + if(!m_pInput) + return NULL; + + DemuxPacket* pPacket = CDVDDemuxUtils::AllocateDemuxPacket(CDDA_READ_SIZE); + + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + pPacket->iSize = m_pInput->Read(pPacket->pData, CDDA_READ_SIZE); + pPacket->iStreamId = 0; + + if(pPacket->iSize < 1) + { + delete pPacket; + pPacket = NULL; + } + else + { + int n = m_stream->iBitRate>>3; + if (n > 0) + { + m_bytes += pPacket->iSize; + pPacket->dts = (double)m_bytes * DVD_TIME_BASE / n; + pPacket->pts = pPacket->dts; + } + else + { + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + } + } + + return pPacket; +} + +bool CDVDDemuxCDDA::SeekTime(int time, bool backwords, double* startpts) +{ + int bytes_per_second = m_stream->iBitRate>>3; + // clamp seeks to bytes per full sample + int clamp_bytes = (m_stream->iBitsPerSample>>3) * m_stream->iChannels; + + // time is in milliseconds + int64_t seekPos = m_pInput->Seek((((int64_t)time * bytes_per_second / 1000) / clamp_bytes ) * clamp_bytes, SEEK_SET) > 0; + if (seekPos > 0) + m_bytes = seekPos; + + if (startpts) + *startpts = (double)m_bytes * DVD_TIME_BASE / bytes_per_second; + + return seekPos > 0; +}; + +int CDVDDemuxCDDA::GetStreamLength() +{ + int64_t num_track_bytes = m_pInput->GetLength(); + int bytes_per_second = (m_stream->iBitRate>>3); + int64_t track_mseconds = num_track_bytes*1000 / bytes_per_second; + return (int)track_mseconds; +} + +CDemuxStream* CDVDDemuxCDDA::GetStream(int iStreamId) +{ + if(iStreamId != 0) + return NULL; + + return m_stream; +} + +int CDVDDemuxCDDA::GetNrOfStreams() +{ + return (m_stream == NULL ? 0 : 1); +} + +std::string CDVDDemuxCDDA::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxCDDA::GetStreamCodecName(int iStreamId, std::string &strName) +{ + if (m_stream && iStreamId == 0) + strName = "pcm"; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h new file mode 100644 index 0000000..6a3c50a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxCDDA.h @@ -0,0 +1,60 @@ +#pragma once +/* + * Copyright (C) 2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include "utils/log.h" + +#ifdef TARGET_WINDOWS +#define __attribute__(dummy_val) +#else +#include +#endif + +class CDemuxStreamAudioCDDA; + +class CDVDDemuxCDDA : public CDVDDemux +{ +public: + + CDVDDemuxCDDA(); + ~CDVDDemuxCDDA(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + void SetSpeed(int iSpeed) {}; + int GetStreamLength() ; + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + +protected: + friend class CDemuxStreamAudioCDDA; + CDVDInputStream* m_pInput; + int64_t m_bytes; + + CDemuxStreamAudioCDDA *m_stream; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp new file mode 100644 index 0000000..f58472f --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp @@ -0,0 +1,1767 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "system.h" +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifdef TARGET_POSIX +#include "stdint.h" +#endif +#include "DVDDemuxFFmpeg.h" +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDInputStreams/DVDInputStreamNavigator.h" +#ifdef HAVE_LIBBLURAY +#include "DVDInputStreams/DVDInputStreamBluray.h" +#endif +#include "DVDInputStreams/DVDInputStreamPVRManager.h" +#include "DVDInputStreams/DVDInputStreamFFmpeg.h" +#include "DVDDemuxUtils.h" +#include "DVDClock.h" // for DVD_TIME_BASE +#include "commons/Exception.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "filesystem/File.h" +#include "filesystem/CurlFile.h" +#include "filesystem/Directory.h" +#include "utils/log.h" +#include "threads/Thread.h" +#include "threads/SystemClock.h" +#include "utils/TimeUtils.h" +#include "utils/StringUtils.h" +#include "URL.h" +#include "cores/FFmpeg.h" + +extern "C" { +#include "libavutil/opt.h" +} + +struct StereoModeConversionMap +{ + const char* name; + const char* mode; +}; + +// we internally use the matroska string representation of stereoscopic modes. +// This struct is a conversion map to convert stereoscopic mode values +// from asf/wmv to the internally used matroska ones +static const struct StereoModeConversionMap WmvToInternalStereoModeMap[] = +{ + { "SideBySideRF", "right_left" }, + { "SideBySideLF", "left_right" }, + { "OverUnderRT", "bottom_top" }, + { "OverUnderLT", "top_bottom" }, + {} +}; + +#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE) + +void CDemuxStreamAudioFFmpeg::GetStreamInfo(std::string& strInfo) +{ + if(!m_stream) return; + char temp[128]; + avcodec_string(temp, 128, m_stream->codec, 0); + strInfo = temp; +} + +void CDemuxStreamAudioFFmpeg::GetStreamName(std::string& strInfo) +{ + if(!m_stream) return; + if(!m_description.empty()) + strInfo = m_description; + else + CDemuxStream::GetStreamName(strInfo); +} + +void CDemuxStreamSubtitleFFmpeg::GetStreamName(std::string& strInfo) +{ + if(!m_stream) return; + if(!m_description.empty()) + strInfo = m_description; + else + CDemuxStream::GetStreamName(strInfo); +} + +void CDemuxStreamVideoFFmpeg::GetStreamInfo(std::string& strInfo) +{ + if(!m_stream) return; + char temp[128]; + avcodec_string(temp, 128, m_stream->codec, 0); + strInfo = temp; +} + +void CDemuxStreamSubtitleFFmpeg::GetStreamInfo(std::string& strInfo) +{ + if(!m_stream) return; + char temp[128]; + avcodec_string(temp, 128, m_stream->codec, 0); + strInfo = temp; +} + +static int interrupt_cb(void* ctx) +{ + CDVDDemuxFFmpeg* demuxer = static_cast(ctx); + if(demuxer && demuxer->Aborted()) + return 1; + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +/* +static int dvd_file_open(URLContext *h, const char *filename, int flags) +{ + return -1; +} +*/ + +static int dvd_file_read(void *h, uint8_t* buf, int size) +{ + if(interrupt_cb(h)) + return AVERROR_EXIT; + + CDVDInputStream* pInputStream = static_cast(h)->m_pInput; + return pInputStream->Read(buf, size); +} +/* +static int dvd_file_write(URLContext *h, uint8_t* buf, int size) +{ + return -1; +} +*/ +static int64_t dvd_file_seek(void *h, int64_t pos, int whence) +{ + if(interrupt_cb(h)) + return AVERROR_EXIT; + + CDVDInputStream* pInputStream = static_cast(h)->m_pInput; + if(whence == AVSEEK_SIZE) + return pInputStream->GetLength(); + else + return pInputStream->Seek(pos, whence & ~AVSEEK_FORCE); +} + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// + +CDVDDemuxFFmpeg::CDVDDemuxFFmpeg() : CDVDDemux() +{ + m_pFormatContext = NULL; + m_pInput = NULL; + m_ioContext = NULL; + m_currentPts = DVD_NOPTS_VALUE; + m_bMatroska = false; + m_bAVI = false; + m_speed = DVD_PLAYSPEED_NORMAL; + m_program = UINT_MAX; + m_pkt.result = -1; + memset(&m_pkt.pkt, 0, sizeof(AVPacket)); + m_streaminfo = true; /* set to true if we want to look for streams before playback */ + m_checkvideo = false; +} + +CDVDDemuxFFmpeg::~CDVDDemuxFFmpeg() +{ + Dispose(); + ff_flush_avutil_log_buffers(); +} + +bool CDVDDemuxFFmpeg::Aborted() +{ + if(m_timeout.IsTimePast()) + return true; + + CDVDInputStreamFFmpeg * input = dynamic_cast(m_pInput); + if(input && input->Aborted()) + return true; + + return false; +} + +bool CDVDDemuxFFmpeg::Open(CDVDInputStream* pInput, bool streaminfo, bool fileinfo) +{ + AVInputFormat* iformat = NULL; + std::string strFile; + m_streaminfo = streaminfo; + m_currentPts = DVD_NOPTS_VALUE; + m_speed = DVD_PLAYSPEED_NORMAL; + m_program = UINT_MAX; + const AVIOInterruptCB int_cb = { interrupt_cb, this }; + + if (!pInput) return false; + + m_pInput = pInput; + strFile = m_pInput->GetFileName(); + + if( m_pInput->GetContent().length() > 0 ) + { + std::string content = m_pInput->GetContent(); + StringUtils::ToLower(content); + + /* check if we can get a hint from content */ + if ( content.compare("video/x-vobsub") == 0 ) + iformat = av_find_input_format("mpeg"); + else if( content.compare("video/x-dvd-mpeg") == 0 ) + iformat = av_find_input_format("mpeg"); + else if( content.compare("video/mp2t") == 0 ) + iformat = av_find_input_format("mpegts"); + else if( content.compare("multipart/x-mixed-replace") == 0 ) + iformat = av_find_input_format("mjpeg"); + } + + // open the demuxer + m_pFormatContext = avformat_alloc_context(); + m_pFormatContext->interrupt_callback = int_cb; + + // try to abort after 30 seconds + m_timeout.Set(30000); + + if( m_pInput->IsStreamType(DVDSTREAM_TYPE_FFMPEG) ) + { + // special stream type that makes avformat handle file opening + // allows internal ffmpeg protocols to be used + CURL url = m_pInput->GetURL(); + + AVDictionary *options = GetFFMpegOptionsFromURL(url); + + int result=-1; + if (url.IsProtocol("mms")) + { + // try mmsh, then mmst + url.SetProtocol("mmsh"); + url.SetProtocolOptions(""); + result = avformat_open_input(&m_pFormatContext, url.Get().c_str(), iformat, &options); + if (result < 0) + { + url.SetProtocol("mmst"); + strFile = url.Get(); + } + } + if (result < 0 && avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0 ) + { + CLog::Log(LOGDEBUG, "Error, could not open file %s", CURL::GetRedacted(strFile).c_str()); + Dispose(); + av_dict_free(&options); + return false; + } + av_dict_free(&options); + } + else + { + unsigned char* buffer = (unsigned char*)av_malloc(FFMPEG_FILE_BUFFER_SIZE); + m_ioContext = avio_alloc_context(buffer, FFMPEG_FILE_BUFFER_SIZE, 0, this, dvd_file_read, NULL, dvd_file_seek); + m_ioContext->max_packet_size = m_pInput->GetBlockSize(); + if(m_ioContext->max_packet_size) + m_ioContext->max_packet_size *= FFMPEG_FILE_BUFFER_SIZE / m_ioContext->max_packet_size; + + if(m_pInput->Seek(0, SEEK_POSSIBLE) == 0) + m_ioContext->seekable = 0; + + std::string content = m_pInput->GetContent(); + StringUtils::ToLower(content); + if (StringUtils::StartsWith(content, "audio/l16")) + iformat = av_find_input_format("s16be"); + + if( iformat == NULL ) + { + // let ffmpeg decide which demuxer we have to open + + bool trySPDIFonly = (m_pInput->GetContent() == "audio/x-spdif-compressed"); + + if (!trySPDIFonly) + av_probe_input_buffer(m_ioContext, &iformat, strFile.c_str(), NULL, 0, 0); + + // Use the more low-level code in case we have been built against an old + // FFmpeg without the above av_probe_input_buffer(), or in case we only + // want to probe for spdif (DTS or IEC 61937) compressed audio + // specifically, or in case the file is a wav which may contain DTS or + // IEC 61937 (e.g. ac3-in-wav) and we want to check for those formats. + if (trySPDIFonly || (iformat && strcmp(iformat->name, "wav") == 0)) + { + AVProbeData pd; + uint8_t probe_buffer[FFMPEG_FILE_BUFFER_SIZE + AVPROBE_PADDING_SIZE]; + + // init probe data + pd.buf = probe_buffer; + pd.filename = strFile.c_str(); + + // av_probe_input_buffer might have changed the buffer_size beyond our allocated amount + int buffer_size = std::min((int) FFMPEG_FILE_BUFFER_SIZE, m_ioContext->buffer_size); + // read data using avformat's buffers + pd.buf_size = avio_read(m_ioContext, pd.buf, m_ioContext->max_packet_size ? m_ioContext->max_packet_size : buffer_size); + if (pd.buf_size <= 0) + { + CLog::Log(LOGERROR, "%s - error reading from input stream, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str()); + return false; + } + memset(pd.buf+pd.buf_size, 0, AVPROBE_PADDING_SIZE); + + // restore position again + avio_seek(m_ioContext , 0, SEEK_SET); + + // the advancedsetting is for allowing the user to force outputting the + // 44.1 kHz DTS wav file as PCM, so that an A/V receiver can decode + // it (this is temporary until we handle 44.1 kHz passthrough properly) + if (trySPDIFonly || (iformat && strcmp(iformat->name, "wav") == 0 && !g_advancedSettings.m_dvdplayerIgnoreDTSinWAV)) + { + // check for spdif and dts + // This is used with wav files and audio CDs that may contain + // a DTS or AC3 track padded for S/PDIF playback. If neither of those + // is present, we assume it is PCM audio. + // AC3 is always wrapped in iec61937 (ffmpeg "spdif"), while DTS + // may be just padded. + AVInputFormat *iformat2; + iformat2 = av_find_input_format("spdif"); + + if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4) + { + iformat = iformat2; + } + else + { + // not spdif or no spdif demuxer, try dts + iformat2 = av_find_input_format("dts"); + + if (iformat2 && iformat2->read_probe(&pd) > AVPROBE_SCORE_MAX / 4) + { + iformat = iformat2; + } + else if (trySPDIFonly) + { + // not dts either, return false in case we were explicitely + // requested to only check for S/PDIF padded compressed audio + CLog::Log(LOGDEBUG, "%s - not spdif or dts file, fallbacking", __FUNCTION__); + return false; + } + } + } + } + + if(!iformat) + { + std::string content = m_pInput->GetContent(); + + /* check if we can get a hint from content */ + if( content.compare("audio/aacp") == 0 ) + iformat = av_find_input_format("aac"); + else if( content.compare("audio/aac") == 0 ) + iformat = av_find_input_format("aac"); + else if( content.compare("video/flv") == 0 ) + iformat = av_find_input_format("flv"); + else if( content.compare("video/x-flv") == 0 ) + iformat = av_find_input_format("flv"); + } + + if (!iformat) + { + CLog::Log(LOGERROR, "%s - error probing input format, %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str()); + return false; + } + else + { + if (iformat->name) + CLog::Log(LOGDEBUG, "%s - probing detected format [%s]", __FUNCTION__, iformat->name); + else + CLog::Log(LOGDEBUG, "%s - probing detected unnamed format", __FUNCTION__); + } + } + + + m_pFormatContext->pb = m_ioContext; + + AVDictionary *options = NULL; + if (iformat->name && (strcmp(iformat->name, "mp3") == 0 || strcmp(iformat->name, "mp2") == 0)) + { + CLog::Log(LOGDEBUG, "%s - setting usetoc to 0 for accurate VBR MP3 seek", __FUNCTION__); + av_dict_set(&options, "usetoc", "0", 0); + } + + if (StringUtils::StartsWith(content, "audio/l16")) + { + int channels = 2; + int samplerate = 44100; + GetL16Parameters(channels, samplerate); + av_dict_set_int(&options, "channels", channels, 0); + av_dict_set_int(&options, "sample_rate", samplerate, 0); + } + + if (avformat_open_input(&m_pFormatContext, strFile.c_str(), iformat, &options) < 0) + { + CLog::Log(LOGERROR, "%s - Error, could not open file %s", __FUNCTION__, CURL::GetRedacted(strFile).c_str()); + Dispose(); + av_dict_free(&options); + return false; + } + av_dict_free(&options); + } + + // Avoid detecting framerate if advancedsettings.xml says so + if (g_advancedSettings.m_videoFpsDetect == 0) + m_pFormatContext->fps_probe_size = 0; + + // analyse very short to speed up mjpeg playback start + if (iformat && (strcmp(iformat->name, "mjpeg") == 0) && m_ioContext->seekable == 0) + av_opt_set_int(m_pFormatContext, "analyzeduration", 500000, 0); + + bool skipCreateStreams = false; + bool isBluray = pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY); + if (iformat && (strcmp(iformat->name, "mpegts") == 0) && !fileinfo && !isBluray) + { + av_opt_set_int(m_pFormatContext, "analyzeduration", 500000, 0); + m_checkvideo = true; + skipCreateStreams = true; + } + else if (!iformat || (strcmp(iformat->name, "mpegts") != 0)) + { + m_streaminfo = true; + } + + // we need to know if this is matroska or avi later + m_bMatroska = strncmp(m_pFormatContext->iformat->name, "matroska", 8) == 0; // for "matroska.webm" + m_bAVI = strcmp(m_pFormatContext->iformat->name, "avi") == 0; + + if (m_streaminfo) + { + /* to speed up dvd switches, only analyse very short */ + if(m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)) + av_opt_set_int(m_pFormatContext, "analyzeduration", 500000, 0); + + CLog::Log(LOGDEBUG, "%s - avformat_find_stream_info starting", __FUNCTION__); + int iErr = avformat_find_stream_info(m_pFormatContext, NULL); + if (iErr < 0) + { + CLog::Log(LOGWARNING,"could not find codec parameters for %s", CURL::GetRedacted(strFile).c_str()); + if (m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD) + || m_pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY) + || (m_pFormatContext->nb_streams == 1 && m_pFormatContext->streams[0]->codec->codec_id == AV_CODEC_ID_AC3)) + { + // special case, our codecs can still handle it. + } + else + { + Dispose(); + return false; + } + } + CLog::Log(LOGDEBUG, "%s - av_find_stream_info finished", __FUNCTION__); + + if (m_checkvideo) + { + // make sure we start video with an i-frame + ResetVideoStreams(); + } + } + else + { + m_program = 0; + m_checkvideo = true; + skipCreateStreams = true; + } + + // reset any timeout + m_timeout.SetInfinite(); + + // if format can be nonblocking, let's use that + m_pFormatContext->flags |= AVFMT_FLAG_NONBLOCK; + + // print some extra information + av_dump_format(m_pFormatContext, 0, strFile.c_str(), 0); + + UpdateCurrentPTS(); + + // in case of mpegts and we have not seen pat/pmt, defer creation of streams + if (!skipCreateStreams || m_pFormatContext->nb_programs > 0) + CreateStreams(); + + // allow IsProgramChange to return true + if (skipCreateStreams && GetNrOfStreams() == 0) + m_program = 0; + + return true; +} + +void CDVDDemuxFFmpeg::Dispose() +{ + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + + if (m_pFormatContext) + { + if (m_ioContext && m_pFormatContext->pb && m_pFormatContext->pb != m_ioContext) + { + CLog::Log(LOGWARNING, "CDVDDemuxFFmpeg::Dispose - demuxer changed our byte context behind our back, possible memleak"); + m_ioContext = m_pFormatContext->pb; + } + avformat_close_input(&m_pFormatContext); + } + + if(m_ioContext) + { + av_free(m_ioContext->buffer); + av_free(m_ioContext); + } + + m_ioContext = NULL; + m_pFormatContext = NULL; + m_speed = DVD_PLAYSPEED_NORMAL; + + DisposeStreams(); + + m_pInput = NULL; +} + +void CDVDDemuxFFmpeg::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream, m_streaminfo); +} + +void CDVDDemuxFFmpeg::Flush() +{ + // naughty usage of an internal ffmpeg function + if (m_pFormatContext) + av_read_frame_flush(m_pFormatContext); + + m_currentPts = DVD_NOPTS_VALUE; + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); +} + +void CDVDDemuxFFmpeg::Abort() +{ + m_timeout.SetExpired(); +} + +void CDVDDemuxFFmpeg::SetSpeed(int iSpeed) +{ + if(!m_pFormatContext) + return; + + if(m_speed != DVD_PLAYSPEED_PAUSE && iSpeed == DVD_PLAYSPEED_PAUSE) + { + m_pInput->Pause(m_currentPts); + av_read_pause(m_pFormatContext); + } + else if(m_speed == DVD_PLAYSPEED_PAUSE && iSpeed != DVD_PLAYSPEED_PAUSE) + { + m_pInput->Pause(m_currentPts); + av_read_play(m_pFormatContext); + } + m_speed = iSpeed; + + AVDiscard discard = AVDISCARD_NONE; + if(m_speed > 4*DVD_PLAYSPEED_NORMAL) + discard = AVDISCARD_NONKEY; + else if(m_speed > 2*DVD_PLAYSPEED_NORMAL) + discard = AVDISCARD_BIDIR; + else if(m_speed < DVD_PLAYSPEED_PAUSE) + discard = AVDISCARD_NONKEY; + + + for(unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + if(m_pFormatContext->streams[i]) + { + if(m_pFormatContext->streams[i]->discard != AVDISCARD_ALL) + m_pFormatContext->streams[i]->discard = discard; + } + } +} + +AVDictionary *CDVDDemuxFFmpeg::GetFFMpegOptionsFromURL(const CURL &url) +{ + + AVDictionary *options = NULL; + + if (url.IsProtocol("http") || url.IsProtocol("https")) + { + std::map protocolOptions; + url.GetProtocolOptions(protocolOptions); + std::string headers; + bool hasUserAgent = false; + for(std::map::const_iterator it = protocolOptions.begin(); it != protocolOptions.end(); ++it) + { + std::string name = it->first; StringUtils::ToLower(name); + const std::string &value = it->second; + + if (name == "seekable") + av_dict_set(&options, "seekable", value.c_str(), 0); + else if (name == "user-agent") + { + av_dict_set(&options, "user-agent", value.c_str(), 0); + hasUserAgent = true; + } + else if (name != "auth" && name != "encoding") + // all other protocol options can be added as http header. + headers.append(it->first).append(": ").append(value).append("\r\n"); + } + if (!hasUserAgent) + // set default xbmc user-agent. + av_dict_set(&options, "user-agent", g_advancedSettings.m_userAgent.c_str(), 0); + + if (!headers.empty()) + av_dict_set(&options, "headers", headers.c_str(), 0); + + std::string cookies; + if (XFILE::CCurlFile::GetCookies(url, cookies)) + av_dict_set(&options, "cookies", cookies.c_str(), 0); + + } + return options; +} + +double CDVDDemuxFFmpeg::ConvertTimestamp(int64_t pts, int den, int num) +{ + if (pts == (int64_t)AV_NOPTS_VALUE) + return DVD_NOPTS_VALUE; + + // do calculations in floats as they can easily overflow otherwise + // we don't care for having a completly exact timestamp anyway + double timestamp = (double)pts * num / den; + double starttime = 0.0f; + + // for dvd's we need the original time + if(CDVDInputStream::IMenus* menu = dynamic_cast(m_pInput)) + starttime = menu->GetTimeStampCorrection() / DVD_TIME_BASE; + else if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) + starttime = (double)m_pFormatContext->start_time / AV_TIME_BASE; + + if(timestamp > starttime) + timestamp -= starttime; + // allow for largest possible difference in pts and dts for a single packet + else if( timestamp + 0.5f > starttime ) + timestamp = 0; + + return timestamp*DVD_TIME_BASE; +} + +DemuxPacket* CDVDDemuxFFmpeg::Read() +{ + DemuxPacket* pPacket = NULL; + // on some cases where the received packet is invalid we will need to return an empty packet (0 length) otherwise the main loop (in CDVDPlayer) + // would consider this the end of stream and stop. + bool bReturnEmpty = false; + { CSingleLock lock(m_critSection); // open lock scope + if (m_pFormatContext) + { + // assume we are not eof + if(m_pFormatContext->pb) + m_pFormatContext->pb->eof_reached = 0; + + // check for saved packet after a program change + if (m_pkt.result < 0) + { + // keep track if ffmpeg doesn't always set these + m_pkt.pkt.size = 0; + m_pkt.pkt.data = NULL; + + // timeout reads after 100ms + m_timeout.Set(20000); + m_pkt.result = av_read_frame(m_pFormatContext, &m_pkt.pkt); + m_timeout.SetInfinite(); + } + + if (m_pkt.result == AVERROR(EINTR) || m_pkt.result == AVERROR(EAGAIN)) + { + // timeout, probably no real error, return empty packet + bReturnEmpty = true; + } + else if (m_pkt.result < 0) + { + Flush(); + } + else if (IsProgramChange()) + { + // update streams + CreateStreams(m_program); + + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(0); + pPacket->iStreamId = DMX_SPECIALID_STREAMCHANGE; + + return pPacket; + } + // check size and stream index for being in a valid range + else if (m_pkt.pkt.size < 0 || + m_pkt.pkt.stream_index < 0 || + m_pkt.pkt.stream_index >= (int)m_pFormatContext->nb_streams) + { + // XXX, in some cases ffmpeg returns a negative packet size + if(m_pFormatContext->pb && !m_pFormatContext->pb->eof_reached) + { + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::Read() no valid packet"); + bReturnEmpty = true; + Flush(); + } + else + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::Read() returned invalid packet and eof reached"); + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + } + else + { + ParsePacket(&m_pkt.pkt); + + AVStream *stream = m_pFormatContext->streams[m_pkt.pkt.stream_index]; + + if (IsVideoReady()) + { + if (m_program != UINT_MAX) + { + /* check so packet belongs to selected program */ + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + if(m_pkt.pkt.stream_index == (int)m_pFormatContext->programs[m_program]->stream_index[i]) + { + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size); + break; + } + } + + if (!pPacket) + bReturnEmpty = true; + } + else + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(m_pkt.pkt.size); + } + else + bReturnEmpty = true; + + if (pPacket) + { + // lavf sometimes bugs out and gives 0 dts/pts instead of no dts/pts + // since this could only happens on initial frame under normal + // circomstances, let's assume it is wrong all the time + if(m_pkt.pkt.dts == 0) + m_pkt.pkt.dts = AV_NOPTS_VALUE; + if(m_pkt.pkt.pts == 0) + m_pkt.pkt.pts = AV_NOPTS_VALUE; + + if(m_bMatroska && stream->codec && stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { // matroska can store different timestamps + // for different formats, for native stored + // stuff it is pts, but for ms compatibility + // tracks, it is really dts. sadly ffmpeg + // sets these two timestamps equal all the + // time, so we select it here instead + if(stream->codec->codec_tag == 0) + m_pkt.pkt.dts = AV_NOPTS_VALUE; + else + m_pkt.pkt.pts = AV_NOPTS_VALUE; + } + + // we need to get duration slightly different for matroska embedded text subtitels + if(m_bMatroska && stream->codec && stream->codec->codec_id == AV_CODEC_ID_TEXT && m_pkt.pkt.convergence_duration != 0) + m_pkt.pkt.duration = m_pkt.pkt.convergence_duration; + + if(m_bAVI && stream->codec && stream->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + // AVI's always have borked pts, specially if m_pFormatContext->flags includes + // AVFMT_FLAG_GENPTS so always use dts + m_pkt.pkt.pts = AV_NOPTS_VALUE; + } + + // copy contents into our own packet + pPacket->iSize = m_pkt.pkt.size; + + // maybe we can avoid a memcpy here by detecting where pkt.destruct is pointing too? + if (m_pkt.pkt.data) + memcpy(pPacket->pData, m_pkt.pkt.data, pPacket->iSize); + + pPacket->pts = ConvertTimestamp(m_pkt.pkt.pts, stream->time_base.den, stream->time_base.num); + pPacket->dts = ConvertTimestamp(m_pkt.pkt.dts, stream->time_base.den, stream->time_base.num); + pPacket->duration = DVD_SEC_TO_TIME((double)m_pkt.pkt.duration * stream->time_base.num / stream->time_base.den); + + // used to guess streamlength + if (pPacket->dts != DVD_NOPTS_VALUE && (pPacket->dts > m_currentPts || m_currentPts == DVD_NOPTS_VALUE)) + m_currentPts = pPacket->dts; + + + // check if stream has passed full duration, needed for live streams + bool bAllowDurationExt = (stream->codec && (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO || stream->codec->codec_type == AVMEDIA_TYPE_AUDIO)); + if(bAllowDurationExt && m_pkt.pkt.dts != (int64_t)AV_NOPTS_VALUE) + { + int64_t duration; + duration = m_pkt.pkt.dts; + if(stream->start_time != (int64_t)AV_NOPTS_VALUE) + duration -= stream->start_time; + + if(duration > stream->duration) + { + stream->duration = duration; + duration = av_rescale_rnd(stream->duration, (int64_t)stream->time_base.num * AV_TIME_BASE, stream->time_base.den, AV_ROUND_NEAR_INF); + if ((m_pFormatContext->duration == (int64_t)AV_NOPTS_VALUE) + || (m_pFormatContext->duration != (int64_t)AV_NOPTS_VALUE && duration > m_pFormatContext->duration)) + m_pFormatContext->duration = duration; + } + } + + // store internal id until we know the continuous id presented to player + // the stream might not have been created yet + pPacket->iStreamId = m_pkt.pkt.stream_index; + } + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + } + } + } // end of lock scope + if (bReturnEmpty && !pPacket) + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(0); + + if (!pPacket) return NULL; + + // check streams, can we make this a bit more simple? + if (pPacket && pPacket->iStreamId >= 0) + { + CDemuxStream *stream = GetStreamInternal(pPacket->iStreamId); + if (!stream || + stream->pPrivate != m_pFormatContext->streams[pPacket->iStreamId] || + stream->codec != m_pFormatContext->streams[pPacket->iStreamId]->codec->codec_id) + { + // content has changed, or stream did not yet exist + stream = AddStream(pPacket->iStreamId); + } + // we already check for a valid m_streams[pPacket->iStreamId] above + else if (stream->type == STREAM_AUDIO) + { + if (((CDemuxStreamAudio*)stream)->iChannels != m_pFormatContext->streams[pPacket->iStreamId]->codec->channels || + ((CDemuxStreamAudio*)stream)->iSampleRate != m_pFormatContext->streams[pPacket->iStreamId]->codec->sample_rate) + { + // content has changed + stream = AddStream(pPacket->iStreamId); + } + } + else if (stream->type == STREAM_VIDEO) + { + if (((CDemuxStreamVideo*)stream)->iWidth != m_pFormatContext->streams[pPacket->iStreamId]->codec->width || + ((CDemuxStreamVideo*)stream)->iHeight != m_pFormatContext->streams[pPacket->iStreamId]->codec->height) + { + // content has changed + stream = AddStream(pPacket->iStreamId); + } + } + if (!stream) + { + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::AddStream - internal error, stream is null"); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + return NULL; + } + // set continuous stream id for player + pPacket->iStreamId = stream->iId; + } + return pPacket; +} + +bool CDVDDemuxFFmpeg::SeekTime(int time, bool backwords, double *startpts) +{ + if (!m_pInput) + return false; + + if(time < 0) + time = 0; + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + + CDVDInputStream::ISeekTime* ist = dynamic_cast(m_pInput); + if (ist) + { + if (!ist->SeekTime(time)) + return false; + + if(startpts) + *startpts = DVD_NOPTS_VALUE; + + Flush(); + + // also empty the internal ffmpeg buffer + m_ioContext->buf_ptr = m_ioContext->buf_end; + + return true; + } + + if(!m_pInput->Seek(0, SEEK_POSSIBLE) + && !m_pInput->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + CLog::Log(LOGDEBUG, "%s - input stream reports it is not seekable", __FUNCTION__); + return false; + } + + int64_t seek_pts = (int64_t)time * (AV_TIME_BASE / 1000); + bool ismp3 = m_pFormatContext->iformat && (strcmp(m_pFormatContext->iformat->name, "mp3") == 0); + if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE && !ismp3) + seek_pts += m_pFormatContext->start_time; + + int ret; + { + CSingleLock lock(m_critSection); + ret = av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); + + // demuxer will return failure, if you seek behind eof + if (ret < 0 && m_pFormatContext->duration && seek_pts >= (m_pFormatContext->duration + m_pFormatContext->start_time)) + ret = 0; + else if (ret < 0 && m_pInput->IsEOF()) + ret = 0; + + if(ret >= 0) + UpdateCurrentPTS(); + } + + if(m_currentPts == DVD_NOPTS_VALUE) + CLog::Log(LOGDEBUG, "%s - unknown position after seek", __FUNCTION__); + else + CLog::Log(LOGDEBUG, "%s - seek ended up on time %d", __FUNCTION__, (int)(m_currentPts / DVD_TIME_BASE * 1000)); + + // in this case the start time is requested time + if(startpts) + *startpts = DVD_MSEC_TO_TIME(time); + + return (ret >= 0); +} + +bool CDVDDemuxFFmpeg::SeekByte(int64_t pos) +{ + CSingleLock lock(m_critSection); + int ret = av_seek_frame(m_pFormatContext, -1, pos, AVSEEK_FLAG_BYTE); + + if(ret >= 0) + UpdateCurrentPTS(); + + m_pkt.result = -1; + av_free_packet(&m_pkt.pkt); + + return (ret >= 0); +} + +void CDVDDemuxFFmpeg::UpdateCurrentPTS() +{ + m_currentPts = DVD_NOPTS_VALUE; + + int idx = av_find_default_stream_index(m_pFormatContext); + if (idx >= 0) + { + AVStream *stream = m_pFormatContext->streams[idx]; + if(stream && stream->cur_dts != (int64_t)AV_NOPTS_VALUE) + { + double ts = ConvertTimestamp(stream->cur_dts, stream->time_base.den, stream->time_base.num); + if(m_currentPts == DVD_NOPTS_VALUE || m_currentPts > ts ) + m_currentPts = ts; + } + } +} + +int CDVDDemuxFFmpeg::GetStreamLength() +{ + if (!m_pFormatContext) + return 0; + + if (m_pFormatContext->duration < 0) + return 0; + + return (int)(m_pFormatContext->duration / (AV_TIME_BASE / 1000)); +} + +/** + * @brief Finds stream based on demuxer index + */ +CDemuxStream* CDVDDemuxFFmpeg::GetStream(int iStreamId) +{ + if(iStreamId >= 0 && (size_t)iStreamId < m_stream_index.size()) + return m_stream_index[iStreamId]->second; + else + return NULL; +} + +/** + * @brief Finds stream based on ffmpeg index + */ +CDemuxStream* CDVDDemuxFFmpeg::GetStreamInternal(int iId) +{ + std::map::iterator it = m_streams.find(iId); + if (it == m_streams.end()) + return NULL; + else + return it->second; +} + +int CDVDDemuxFFmpeg::GetNrOfStreams() +{ + return m_stream_index.size(); +} + +static double SelectAspect(AVStream* st, bool* forced) +{ + *forced = false; + /* if stream aspect is 1:1 or 0:0 use codec aspect */ + if((st->sample_aspect_ratio.den == 1 || st->sample_aspect_ratio.den == 0) + && (st->sample_aspect_ratio.num == 1 || st->sample_aspect_ratio.num == 0) + && st->codec->sample_aspect_ratio.num != 0) + return av_q2d(st->codec->sample_aspect_ratio); + + *forced = true; + if(st->sample_aspect_ratio.num != 0) + return av_q2d(st->sample_aspect_ratio); + + return 0.0; +} + +void CDVDDemuxFFmpeg::CreateStreams(unsigned int program) +{ + DisposeStreams(); + + // add the ffmpeg streams to our own stream map + if (m_pFormatContext->nb_programs) + { + // check if desired program is available + if (program < m_pFormatContext->nb_programs && m_pFormatContext->programs[program]->nb_stream_indexes > 0) + { + m_program = program; + } + else + m_program = UINT_MAX; + + // look for first non empty stream and discard nonselected programs + for (unsigned int i = 0; i < m_pFormatContext->nb_programs; i++) + { + if(m_program == UINT_MAX && m_pFormatContext->programs[i]->nb_stream_indexes > 0) + { + m_program = i; + } + + if(i != m_program) + m_pFormatContext->programs[i]->discard = AVDISCARD_ALL; + } + if(m_program != UINT_MAX) + { + // add streams from selected program + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + AddStream(m_pFormatContext->programs[m_program]->stream_index[i]); + } + } + else + m_program = UINT_MAX; + + // if there were no programs or they were all empty, add all streams + if (m_program == UINT_MAX) + { + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + AddStream(i); + } +} + +void CDVDDemuxFFmpeg::DisposeStreams() +{ + std::map::iterator it; + for(it = m_streams.begin(); it != m_streams.end(); ++it) + delete it->second; + m_streams.clear(); + m_stream_index.clear(); +} + +CDemuxStream* CDVDDemuxFFmpeg::AddStream(int iId) +{ + AVStream* pStream = m_pFormatContext->streams[iId]; + if (pStream) + { + CDemuxStream* stream = NULL; + + switch (pStream->codec->codec_type) + { + case AVMEDIA_TYPE_AUDIO: + { + CDemuxStreamAudioFFmpeg* st = new CDemuxStreamAudioFFmpeg(this, pStream); + stream = st; + st->iChannels = pStream->codec->channels; + st->iSampleRate = pStream->codec->sample_rate; + st->iBlockAlign = pStream->codec->block_align; + st->iBitRate = pStream->codec->bit_rate; + st->iBitsPerSample = pStream->codec->bits_per_raw_sample; + if (st->iBitsPerSample == 0) + st->iBitsPerSample = pStream->codec->bits_per_coded_sample; + + if(av_dict_get(pStream->metadata, "title", NULL, 0)) + st->m_description = av_dict_get(pStream->metadata, "title", NULL, 0)->value; + + break; + } + case AVMEDIA_TYPE_VIDEO: + { + CDemuxStreamVideoFFmpeg* st = new CDemuxStreamVideoFFmpeg(this, pStream); + stream = st; + if(strcmp(m_pFormatContext->iformat->name, "flv") == 0) + st->bVFR = true; + else + st->bVFR = false; + + // never trust pts in avi files with h264. + if (m_bAVI && pStream->codec->codec_id == AV_CODEC_ID_H264) + st->bPTSInvalid = true; + +#if defined(AVFORMAT_HAS_STREAM_GET_R_FRAME_RATE) + AVRational r_frame_rate = av_stream_get_r_frame_rate(pStream); +#else + AVRational r_frame_rate = pStream->r_frame_rate; +#endif + + //average fps is more accurate for mkv files + if (m_bMatroska && pStream->avg_frame_rate.den && pStream->avg_frame_rate.num) + { + st->iFpsRate = pStream->avg_frame_rate.num; + st->iFpsScale = pStream->avg_frame_rate.den; + } + else if(r_frame_rate.den && r_frame_rate.num) + { + st->iFpsRate = r_frame_rate.num; + st->iFpsScale = r_frame_rate.den; + } + else + { + st->iFpsRate = 0; + st->iFpsScale = 0; + } + + // added for aml hw decoder, mkv frame-rate can be wrong. + if (r_frame_rate.den && r_frame_rate.num) + { + st->irFpsRate = r_frame_rate.num; + st->irFpsScale = r_frame_rate.den; + } + else + { + st->irFpsRate = 0; + st->irFpsScale = 0; + } + + if (pStream->codec_info_nb_frames > 0 + && pStream->codec_info_nb_frames <= 2 + && m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)) + { + CLog::Log(LOGDEBUG, "%s - fps may be unreliable since ffmpeg decoded only %d frame(s)", __FUNCTION__, pStream->codec_info_nb_frames); + st->iFpsRate = 0; + st->iFpsScale = 0; + } + + st->iWidth = pStream->codec->width; + st->iHeight = pStream->codec->height; + st->fAspect = SelectAspect(pStream, &st->bForcedAspect) * pStream->codec->width / pStream->codec->height; + st->iOrientation = 0; + st->iBitsPerPixel = pStream->codec->bits_per_coded_sample; + + AVDictionaryEntry *rtag = av_dict_get(pStream->metadata, "rotate", NULL, 0); + if (rtag) + st->iOrientation = atoi(rtag->value); + + // detect stereoscopic mode + std::string stereoMode = GetStereoModeFromMetadata(pStream->metadata); + // check for metadata in file if detection in stream failed + if (stereoMode.empty()) + stereoMode = GetStereoModeFromMetadata(m_pFormatContext->metadata); + if (!stereoMode.empty()) + st->stereo_mode = stereoMode; + + + if ( m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD) ) + { + if (pStream->codec->codec_id == AV_CODEC_ID_PROBE) + { + // fix MPEG-1/MPEG-2 video stream probe returning AV_CODEC_ID_PROBE for still frames. + // ffmpeg issue 1871, regression from ffmpeg r22831. + if ((pStream->id & 0xF0) == 0xE0) + { + pStream->codec->codec_id = AV_CODEC_ID_MPEG2VIDEO; + pStream->codec->codec_tag = MKTAG('M','P','2','V'); + CLog::Log(LOGERROR, "%s - AV_CODEC_ID_PROBE detected, forcing AV_CODEC_ID_MPEG2VIDEO", __FUNCTION__); + } + } + } + break; + } + case AVMEDIA_TYPE_DATA: + { + stream = new CDemuxStream(); + stream->type = STREAM_DATA; + break; + } + case AVMEDIA_TYPE_SUBTITLE: + { + if (pStream->codec->codec_id == AV_CODEC_ID_DVB_TELETEXT && CSettings::Get().GetBool("videoplayer.teletextenabled")) + { + CDemuxStreamTeletext* st = new CDemuxStreamTeletext(); + stream = st; + stream->type = STREAM_TELETEXT; + break; + } + else + { + CDemuxStreamSubtitleFFmpeg* st = new CDemuxStreamSubtitleFFmpeg(this, pStream); + stream = st; + + if(av_dict_get(pStream->metadata, "title", NULL, 0)) + st->m_description = av_dict_get(pStream->metadata, "title", NULL, 0)->value; + + break; + } + } + case AVMEDIA_TYPE_ATTACHMENT: + { //mkv attachments. Only bothering with fonts for now. + if(pStream->codec->codec_id == AV_CODEC_ID_TTF + || pStream->codec->codec_id == AV_CODEC_ID_OTF + ) + { + std::string fileName = "special://temp/fonts/"; + XFILE::CDirectory::Create(fileName); + AVDictionaryEntry *nameTag = av_dict_get(pStream->metadata, "filename", NULL, 0); + if (!nameTag) + { + CLog::Log(LOGERROR, "%s: TTF attachment has no name", __FUNCTION__); + } + else + { + fileName += nameTag->value; + XFILE::CFile file; + if(pStream->codec->extradata && file.OpenForWrite(fileName)) + { + if (file.Write(pStream->codec->extradata, pStream->codec->extradata_size) != pStream->codec->extradata_size) + { + file.Close(); + XFILE::CFile::Delete(fileName); + CLog::Log(LOGDEBUG, "%s: Error saving font file \"%s\"", __FUNCTION__, fileName.c_str()); + } + } + } + } + stream = new CDemuxStream(); + stream->type = STREAM_NONE; + break; + } + default: + { + stream = new CDemuxStream(); + stream->type = STREAM_NONE; + break; + } + } + + // set ffmpeg type + stream->orig_type = pStream->codec->codec_type; + + // generic stuff + if (pStream->duration != (int64_t)AV_NOPTS_VALUE) + stream->iDuration = (int)((pStream->duration / AV_TIME_BASE) & 0xFFFFFFFF); + + stream->codec = pStream->codec->codec_id; + stream->codec_fourcc = pStream->codec->codec_tag; + stream->profile = pStream->codec->profile; + stream->level = pStream->codec->level; + + stream->source = STREAM_SOURCE_DEMUX; + stream->pPrivate = pStream; + stream->flags = (CDemuxStream::EFlags)pStream->disposition; + + AVDictionaryEntry *langTag = av_dict_get(pStream->metadata, "language", NULL, 0); + if (langTag) + strncpy(stream->language, langTag->value, 3); + + if( pStream->codec->extradata && pStream->codec->extradata_size > 0 ) + { + stream->ExtraSize = pStream->codec->extradata_size; + stream->ExtraData = new uint8_t[pStream->codec->extradata_size]; + memcpy(stream->ExtraData, pStream->codec->extradata, pStream->codec->extradata_size); + } + +#ifdef HAVE_LIBBLURAY + if( m_pInput->IsStreamType(DVDSTREAM_TYPE_BLURAY) ) + static_cast(m_pInput)->GetStreamInfo(pStream->id, stream->language); +#endif + if( m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD) ) + { + // this stuff is really only valid for dvd's. + // this is so that the physicalid matches the + // id's reported from libdvdnav + switch(stream->codec) + { + case AV_CODEC_ID_AC3: + stream->iPhysicalId = pStream->id - 128; + break; + case AV_CODEC_ID_DTS: + stream->iPhysicalId = pStream->id - 136; + break; + case AV_CODEC_ID_MP2: + stream->iPhysicalId = pStream->id - 448; + break; + case AV_CODEC_ID_PCM_S16BE: + stream->iPhysicalId = pStream->id - 160; + break; + case AV_CODEC_ID_DVD_SUBTITLE: + stream->iPhysicalId = pStream->id - 0x20; + break; + default: + stream->iPhysicalId = pStream->id & 0x1f; + break; + } + } + else + stream->iPhysicalId = pStream->id; + + AddStream(iId, stream); + return stream; + } + else + return NULL; +} + +/** + * @brief Adds or updates a demux stream based in ffmpeg id + */ +void CDVDDemuxFFmpeg::AddStream(int iId, CDemuxStream* stream) +{ + std::pair::iterator, bool> res; + + res = m_streams.insert(std::make_pair(iId, stream)); + if(res.second) + { + /* was new stream */ + stream->iId = m_stream_index.size(); + m_stream_index.push_back(res.first); + } + else + { + /* replace old stream, keeping old index */ + stream->iId = res.first->second->iId; + + delete res.first->second; + res.first->second = stream; + } + if(g_advancedSettings.m_logLevel > LOG_LEVEL_NORMAL) + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::AddStream(%d, ...) -> %d", iId, stream->iId); +} + + +std::string CDVDDemuxFFmpeg::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +int CDVDDemuxFFmpeg::GetChapterCount() +{ + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + return ich->GetChapterCount(); + + if(m_pFormatContext == NULL) + return 0; + + return m_pFormatContext->nb_chapters; +} + +int CDVDDemuxFFmpeg::GetChapter() +{ + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + return ich->GetChapter(); + + if(m_pFormatContext == NULL + || m_currentPts == DVD_NOPTS_VALUE) + return 0; + + for(unsigned i = 0; i < m_pFormatContext->nb_chapters; i++) + { + AVChapter *chapter = m_pFormatContext->chapters[i]; + if(m_currentPts >= ConvertTimestamp(chapter->start, chapter->time_base.den, chapter->time_base.num) + && m_currentPts < ConvertTimestamp(chapter->end, chapter->time_base.den, chapter->time_base.num)) + return i + 1; + } + + return 0; +} + +void CDVDDemuxFFmpeg::GetChapterName(std::string& strChapterName, int chapterIdx) +{ + if (chapterIdx <= 0 || chapterIdx > GetChapterCount()) + chapterIdx = GetChapter(); + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + ich->GetChapterName(strChapterName, chapterIdx); + else + { + if(chapterIdx <= 0) + return; + + AVDictionaryEntry *titleTag = av_dict_get(m_pFormatContext->chapters[chapterIdx-1]->metadata, + "title", NULL, 0); + if (titleTag) + strChapterName = titleTag->value; + } +} + +int64_t CDVDDemuxFFmpeg::GetChapterPos(int chapterIdx) +{ + if (chapterIdx <= 0 || chapterIdx > GetChapterCount()) + chapterIdx = GetChapter(); + if(chapterIdx <= 0) + return 0; + + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + return ich->GetChapterPos(chapterIdx); + + return m_pFormatContext->chapters[chapterIdx-1]->start*av_q2d(m_pFormatContext->chapters[chapterIdx-1]->time_base); +} + +bool CDVDDemuxFFmpeg::SeekChapter(int chapter, double* startpts) +{ + if(chapter < 1) + chapter = 1; + + CDVDInputStream::IChapter* ich = dynamic_cast(m_pInput); + if(ich) + { + CLog::Log(LOGDEBUG, "%s - chapter seeking using input stream", __FUNCTION__); + if(!ich->SeekChapter(chapter)) + return false; + + if(startpts) + { + *startpts = DVD_SEC_TO_TIME(ich->GetChapterPos(chapter)); + } + + Flush(); + return true; + } + + if(m_pFormatContext == NULL) + return false; + + if(chapter < 1 || chapter > (int)m_pFormatContext->nb_chapters) + return false; + + AVChapter *ch = m_pFormatContext->chapters[chapter-1]; + double dts = ConvertTimestamp(ch->start, ch->time_base.den, ch->time_base.num); + return SeekTime(DVD_TIME_TO_MSEC(dts), true, startpts); +} + +void CDVDDemuxFFmpeg::GetStreamCodecName(int iStreamId, std::string &strName) +{ + CDemuxStream *stream = GetStream(iStreamId); + if (stream) + { + unsigned int in = stream->codec_fourcc; + // FourCC codes are only valid on video streams, audio codecs in AVI/WAV + // are 2 bytes and audio codecs in transport streams have subtle variation + // e.g AC-3 instead of ac3 + if (stream->type == STREAM_VIDEO && in != 0) + { + char fourcc[5]; +#if defined(__powerpc__) + fourcc[0] = in & 0xff; + fourcc[1] = (in >> 8) & 0xff; + fourcc[2] = (in >> 16) & 0xff; + fourcc[3] = (in >> 24) & 0xff; +#else + memcpy(fourcc, &in, 4); +#endif + fourcc[4] = 0; + // fourccs have to be 4 characters + if (strlen(fourcc) == 4) + { + strName = fourcc; + StringUtils::ToLower(strName); + return; + } + } + +#ifdef FF_PROFILE_DTS_HD_MA + /* use profile to determine the DTS type */ + if (stream->codec == AV_CODEC_ID_DTS) + { + if (stream->profile == FF_PROFILE_DTS_HD_MA) + strName = "dtshd_ma"; + else if (stream->profile == FF_PROFILE_DTS_HD_HRA) + strName = "dtshd_hra"; + else + strName = "dca"; + return; + } +#endif + + AVCodec *codec = avcodec_find_decoder(stream->codec); + if (codec) + strName = codec->name; + } +} + +bool CDVDDemuxFFmpeg::IsProgramChange() +{ + if (m_program == UINT_MAX) + return false; + + if (m_program == 0 && !m_pFormatContext->nb_programs) + return false; + + if(m_pFormatContext->programs[m_program]->nb_stream_indexes != m_streams.size()) + return true; + + if (m_program >= m_pFormatContext->nb_programs) + return true; + + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + int idx = m_pFormatContext->programs[m_program]->stream_index[i]; + CDemuxStream *stream = GetStreamInternal(idx); + if(!stream) + return true; + if(m_pFormatContext->streams[idx]->codec->codec_type != stream->orig_type) + return true; + } + return false; +} + +std::string CDVDDemuxFFmpeg::GetStereoModeFromMetadata(AVDictionary *pMetadata) +{ + std::string stereoMode; + AVDictionaryEntry *tag = NULL; + + // matroska + tag = av_dict_get(pMetadata, "stereo_mode", NULL, 0); + if (tag && tag->value) + stereoMode = tag->value; + + // asf / wmv + if (stereoMode.empty()) + { + tag = av_dict_get(pMetadata, "Stereoscopic", NULL, 0); + if (tag && tag->value) + { + tag = av_dict_get(pMetadata, "StereoscopicLayout", NULL, 0); + if (tag && tag->value) + stereoMode = ConvertCodecToInternalStereoMode(tag->value, WmvToInternalStereoModeMap); + } + } + + return stereoMode; +} + +std::string CDVDDemuxFFmpeg::ConvertCodecToInternalStereoMode(const std::string &mode, const StereoModeConversionMap *conversionMap) +{ + size_t i = 0; + while (conversionMap[i].name) + { + if (mode == conversionMap[i].name) + return conversionMap[i].mode; + i++; + } + return ""; +} + +void CDVDDemuxFFmpeg::ParsePacket(AVPacket *pkt) +{ + AVStream *st = m_pFormatContext->streams[pkt->stream_index]; + CDemuxStream *stream = GetStreamInternal(pkt->stream_index); + + // if the stream is new, tell ffmpeg to parse the stream + if (!stream && !st->parser) + { + st->need_parsing = AVSTREAM_PARSE_FULL; + } + + // split extradata + if(st->parser && st->parser->parser->split && !st->codec->extradata) + { + int i = st->parser->parser->split(st->codec, pkt->data, pkt->size); + if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) + { + // Found extradata, fill it in. This will cause + // a new stream to be created and used. + st->codec->extradata_size = i; + st->codec->extradata = (uint8_t*)av_malloc(st->codec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) + { + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::Read() fetching extradata, extradata_size(%d)", st->codec->extradata_size); + memcpy(st->codec->extradata, pkt->data, st->codec->extradata_size); + memset(st->codec->extradata + i, 0, FF_INPUT_BUFFER_PADDING_SIZE); + } + else + { + st->codec->extradata_size = 0; + } + } + } + + // for video we need a decoder to get desired information into codec context + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && st->codec->extradata && + (!st->codec->width || st->codec->pix_fmt == PIX_FMT_NONE)) + { + // open a decoder, it will be cleared down by ffmpeg on closing the stream + if (!st->codec->codec) + { + const AVCodec* codec; + AVDictionary *thread_opt = NULL; + codec = avcodec_find_decoder(st->codec->codec_id); + // Force thread count to 1 since the h264 decoder will not extract + // SPS and PPS to extradata during multi-threaded decoding + av_dict_set(&thread_opt, "threads", "1", 0); + int res = avcodec_open2(st->codec, codec, &thread_opt); + if(res < 0) + CLog::Log(LOGERROR, "CDVDDemuxFFmpeg::ParsePacket() unable to open codec %d", res); + av_dict_free(&thread_opt); + } + + // We don't need to actually decode here + // we just want to transport SPS data into codec context + st->codec->skip_idct = AVDISCARD_ALL; + // extradata is not decoded if skip_frame >= AVDISCARD_NONREF +// st->codec->skip_frame = AVDISCARD_ALL; + st->codec->skip_loop_filter = AVDISCARD_ALL; + + // We are looking for an IDR frame + AVFrame picture; + memset(&picture, 0, sizeof(AVFrame)); + picture.pts = picture.pkt_dts = picture.pkt_pts = picture.best_effort_timestamp = AV_NOPTS_VALUE; + picture.pkt_pos = -1; + picture.key_frame = 1; + picture.format = -1; + + int got_picture = 0; + avcodec_decode_video2(st->codec, &picture, &got_picture, pkt); + } +} + +bool CDVDDemuxFFmpeg::IsVideoReady() +{ + AVStream *st; + bool hasVideo = false; + + if(!m_checkvideo) + return true; + + if (m_program == 0 && !m_pFormatContext->nb_programs) + return false; + + if(m_program != UINT_MAX) + { + for (unsigned int i = 0; i < m_pFormatContext->programs[m_program]->nb_stream_indexes; i++) + { + int idx = m_pFormatContext->programs[m_program]->stream_index[i]; + st = m_pFormatContext->streams[idx]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + if (st->codec->width && st->codec->pix_fmt != PIX_FMT_NONE) + return true; + hasVideo = true; + } + } + } + else + { + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + st = m_pFormatContext->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + if (st->codec->width && st->codec->pix_fmt != PIX_FMT_NONE) + return true; + hasVideo = true; + } + } + } + return !hasVideo; +} + +void CDVDDemuxFFmpeg::ResetVideoStreams() +{ + AVStream *st; + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + st = m_pFormatContext->streams[i]; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + { + if (st->codec->extradata) + av_free(st->codec->extradata); + st->codec->extradata = NULL; + st->codec->width = 0; + } + } +} + +void CDVDDemuxFFmpeg::GetL16Parameters(int &channels, int &samplerate) +{ + std::string content; + if (XFILE::CCurlFile::GetContentType(m_pInput->GetURL(), content)) + { + StringUtils::ToLower(content); + const size_t len = content.length(); + size_t pos = content.find(';'); + while (pos < len) + { + // move to the next non-whitespace character + pos = content.find_first_not_of(" \t", pos + 1); + + if (pos != std::string::npos) + { + if (content.compare(pos, 9, "channels=", 9) == 0) + { + pos += 9; // move position to char after 'channels=' + size_t len = content.find(';', pos); + if (len != std::string::npos) + len -= pos; + std::string no_channels(content, pos, len); + // as we don't support any charset with ';' in name + StringUtils::Trim(no_channels, " \t"); + if (!no_channels.empty()) + { + int val = strtol(no_channels.c_str(), NULL, 0); + if (val > 0) + channels = val; + else + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::%s - no parameter for channels", __FUNCTION__); + } + } + else if (content.compare(pos, 5, "rate=", 5) == 0) + { + pos += 5; // move position to char after 'rate=' + size_t len = content.find(';', pos); + if (len != std::string::npos) + len -= pos; + std::string rate(content, pos, len); + // as we don't support any charset with ';' in name + StringUtils::Trim(rate, " \t"); + if (!rate.empty()) + { + int val = strtol(rate.c_str(), NULL, 0); + if (val > 0) + samplerate = val; + else + CLog::Log(LOGDEBUG, "CDVDDemuxFFmpeg::%s - no parameter for samplerate", __FUNCTION__); + } + } + pos = content.find(';', pos); // find next parameter + } + } + } +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h new file mode 100644 index 0000000..d180e40 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h @@ -0,0 +1,172 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include "threads/CriticalSection.h" +#include "threads/SystemClock.h" +#include +#include + +extern "C" { +#include "libavformat/avformat.h" +} + +class CDVDDemuxFFmpeg; +class CURL; + +class CDemuxStreamVideoFFmpeg + : public CDemuxStreamVideo +{ + CDVDDemuxFFmpeg *m_parent; + AVStream* m_stream; +public: + CDemuxStreamVideoFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream) + : m_parent(parent) + , m_stream(stream) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + + +class CDemuxStreamAudioFFmpeg + : public CDemuxStreamAudio +{ + CDVDDemuxFFmpeg *m_parent; + AVStream* m_stream; +public: + CDemuxStreamAudioFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream) + : m_parent(parent) + , m_stream(stream) + {} + std::string m_description; + + virtual void GetStreamInfo(std::string& strInfo); + virtual void GetStreamName(std::string& strInfo); +}; + +class CDemuxStreamSubtitleFFmpeg + : public CDemuxStreamSubtitle +{ + CDVDDemuxFFmpeg *m_parent; + AVStream* m_stream; +public: + CDemuxStreamSubtitleFFmpeg(CDVDDemuxFFmpeg *parent, AVStream* stream) + : m_parent(parent) + , m_stream(stream) + {} + std::string m_description; + + virtual void GetStreamInfo(std::string& strInfo); + virtual void GetStreamName(std::string& strInfo); + +}; + +#define FFMPEG_FILE_BUFFER_SIZE 32768 // default reading size for ffmpeg +#define FFMPEG_DVDNAV_BUFFER_SIZE 2048 // for dvd's + +struct StereoModeConversionMap; + +class CDVDDemuxFFmpeg : public CDVDDemux +{ +public: + CDVDDemuxFFmpeg(); + virtual ~CDVDDemuxFFmpeg(); + + bool Open(CDVDInputStream* pInput, bool streaminfo = true, bool fileinfo = false); + void Dispose(); + void Reset(); + void Flush(); + void Abort(); + void SetSpeed(int iSpeed); + virtual std::string GetFileName(); + + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + bool SeekByte(int64_t pos); + int GetStreamLength(); + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + + bool SeekChapter(int chapter, double* startpts = NULL); + int GetChapterCount(); + int GetChapter(); + void GetChapterName(std::string& strChapterName, int chapterIdx=-1); + int64_t GetChapterPos(int chapterIdx=-1); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + + bool Aborted(); + + AVFormatContext* m_pFormatContext; + CDVDInputStream* m_pInput; + +protected: + friend class CDemuxStreamAudioFFmpeg; + friend class CDemuxStreamVideoFFmpeg; + friend class CDemuxStreamSubtitleFFmpeg; + + int ReadFrame(AVPacket *packet); + CDemuxStream* AddStream(int iId); + void AddStream(int iId, CDemuxStream* stream); + CDemuxStream* GetStreamInternal(int iStreamId); + void CreateStreams(unsigned int program = UINT_MAX); + void DisposeStreams(); + void ParsePacket(AVPacket *pkt); + bool IsVideoReady(); + void ResetVideoStreams(); + + AVDictionary *GetFFMpegOptionsFromURL(const CURL &url); + double ConvertTimestamp(int64_t pts, int den, int num); + void UpdateCurrentPTS(); + bool IsProgramChange(); + + std::string GetStereoModeFromMetadata(AVDictionary *pMetadata); + std::string ConvertCodecToInternalStereoMode(const std::string &mode, const StereoModeConversionMap *conversionMap); + + void GetL16Parameters(int &channels, int &samplerate); + + CCriticalSection m_critSection; + std::map m_streams; + std::vector::iterator> m_stream_index; + + AVIOContext* m_ioContext; + + double m_currentPts; // used for stream length estimation + bool m_bMatroska; + bool m_bAVI; + int m_speed; + unsigned m_program; + XbmcThreads::EndTime m_timeout; + + // Due to limitations of ffmpeg, we only can detect a program change + // with a packet. This struct saves the packet for the next read and + // signals STREAMCHANGE to player + struct + { + AVPacket pkt; // packet ffmpeg returned + int result; // result from av_read_packet + }m_pkt; + + bool m_streaminfo; + bool m_checkvideo; +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp new file mode 100644 index 0000000..3674416 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + + +#include "DVDCodecs/DVDCodecs.h" +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDInputStreams/DVDInputStreamHTSP.h" +#include "DVDDemuxHTSP.h" +#include "DVDDemuxUtils.h" +#include "DVDClock.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include + +extern "C" { +#include "lib/libhts/net.h" +#include "lib/libhts/htsmsg.h" +#include "lib/libhts/htsmsg_binary.h" +} + +using namespace std; +using namespace HTSP; + +class CDemuxStreamVideoHTSP + : public CDemuxStreamVideo +{ + CDVDDemuxHTSP *m_parent; + string m_codec; +public: + CDemuxStreamVideoHTSP(CDVDDemuxHTSP *parent, const string& codec) + : m_parent(parent) + , m_codec(codec) + {} + void GetStreamInfo(std::string& strInfo) + { + strInfo = StringUtils::Format("%s, delay: %u, drops: %ub %up %ui" + , m_codec.c_str() + , m_parent->m_QueueStatus.delay + , m_parent->m_QueueStatus.bdrops + , m_parent->m_QueueStatus.pdrops + , m_parent->m_QueueStatus.idrops); + } +}; + +class CDemuxStreamAudioHTSP + : public CDemuxStreamAudio +{ + CDVDDemuxHTSP *m_parent; + string m_codec; +public: + CDemuxStreamAudioHTSP(CDVDDemuxHTSP *parent, const string& codec) + : m_parent(parent) + , m_codec(codec) + + {} + void GetStreamInfo(string& strInfo) + { + strInfo = StringUtils::Format("%s", m_codec.c_str()); + } +}; + +CDVDDemuxHTSP::CDVDDemuxHTSP() + : CDVDDemux() + , m_Input(NULL) + , m_StatusCount(0) +{ +} + +CDVDDemuxHTSP::~CDVDDemuxHTSP() +{ + Dispose(); +} + +bool CDVDDemuxHTSP::Open(CDVDInputStream* input) +{ + Dispose(); + + if(!input->IsStreamType(DVDSTREAM_TYPE_HTSP)) + return false; + + m_Input = (CDVDInputStreamHTSP*)input; + m_StatusCount = 0; + + while(m_Streams.empty() && m_StatusCount == 0) + { + DemuxPacket* pkg = Read(); + if(!pkg) + return false; + CDVDDemuxUtils::FreeDemuxPacket(pkg); + } + + return true; +} + +void CDVDDemuxHTSP::Dispose() +{ +} + +void CDVDDemuxHTSP::Reset() +{ +} + + +void CDVDDemuxHTSP::Flush() +{ +} + +bool CDVDDemuxHTSP::ReadStream(uint8_t* buf, int len) +{ + while(len > 0) + { + int ret = m_Input->Read(buf, len); + if(ret <= 0) + return false; + len -= ret; + buf += ret; + } + return true; +} + +htsmsg_t* CDVDDemuxHTSP::ReadStream() +{ + if(m_Input->IsStreamType(DVDSTREAM_TYPE_HTSP)) + return ((CDVDInputStreamHTSP*)m_Input)->ReadStream(); + + uint32_t l; + if(!ReadStream((uint8_t*)&l, 4)) + return NULL; + + l = ntohl(l); + if(l == 0) + return htsmsg_create_map(); + + uint8_t* buf = (uint8_t*)malloc(l); + if(!buf) + return NULL; + + if(!ReadStream(buf, l)) + return NULL; + + return htsmsg_binary_deserialize(buf, l, buf); /* consumes 'buf' */ +} + +DemuxPacket* CDVDDemuxHTSP::Read() +{ + htsmsg_t * msg; + while((msg = ReadStream())) + { + const char* method = htsmsg_get_str(msg, "method"); + if(method == NULL) + break; + + if (strcmp("subscriptionStart", method) == 0) + SubscriptionStart(msg); + else if(strcmp("subscriptionStop", method) == 0) + SubscriptionStop (msg); + else if(strcmp("subscriptionStatus", method) == 0) + SubscriptionStatus(msg); + else if(strcmp("queueStatus" , method) == 0) + CHTSPSession::ParseQueueStatus(msg, m_QueueStatus); + else if(strcmp("muxpkt" , method) == 0) + { + uint32_t index, duration; + const void* bin; + size_t binlen; + int64_t ts; + + if(htsmsg_get_u32(msg, "stream" , &index) || + htsmsg_get_bin(msg, "payload", &bin, &binlen)) + break; + + DemuxPacket* pkt = CDVDDemuxUtils::AllocateDemuxPacket(binlen); + + memcpy(pkt->pData, bin, binlen); + pkt->iSize = binlen; + + if(!htsmsg_get_u32(msg, "duration", &duration)) + pkt->duration = (double)duration * DVD_TIME_BASE / 1000000; + + if(!htsmsg_get_s64(msg, "dts", &ts)) + pkt->dts = (double)ts * DVD_TIME_BASE / 1000000; + else + pkt->dts = DVD_NOPTS_VALUE; + + if(!htsmsg_get_s64(msg, "pts", &ts)) + pkt->pts = (double)ts * DVD_TIME_BASE / 1000000; + else + pkt->pts = DVD_NOPTS_VALUE; + + pkt->iStreamId = -1; + for(int i = 0; i < (int)m_Streams.size(); i++) + { + if(m_Streams[i]->iPhysicalId == (int)index) + { + pkt->iStreamId = i; + break; + } + } + + htsmsg_destroy(msg); + return pkt; + } + + break; + } + + if(msg) + { + htsmsg_destroy(msg); + return CDVDDemuxUtils::AllocateDemuxPacket(0); + } + return NULL; +} + +void CDVDDemuxHTSP::SubscriptionStart (htsmsg_t *m) +{ + htsmsg_t *streams; + htsmsg_t *info; + htsmsg_field_t *f; + + if((info = htsmsg_get_map(m, "sourceinfo"))) + { + HTSMSG_FOREACH(f, info) + { + if(f->hmf_type != HMF_STR) + continue; + CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - %s: %s", f->hmf_name, htsmsg_field_get_string(f)); + } + } + + if((streams = htsmsg_get_list(m, "streams")) == NULL) + { + CLog::Log(LOGERROR, "CDVDDemuxHTSP::SubscriptionStart - malformed message"); + return; + } + + for(int i = 0; i < (int)m_Streams.size(); i++) + delete m_Streams[i]; + m_Streams.clear(); + + HTSMSG_FOREACH(f, streams) + { + uint32_t index; + const char* type; + const char* lang; + htsmsg_t* sub; + + if(f->hmf_type != HMF_MAP) + continue; + sub = &f->hmf_msg; + + if((type = htsmsg_get_str(sub, "type")) == NULL) + continue; + + if(htsmsg_get_u32(sub, "index", &index)) + continue; + + union { + CDemuxStream* g; + CDemuxStreamAudio* a; + CDemuxStreamVideo* v; + CDemuxStreamSubtitle* s; + CDemuxStreamTeletext* t; + } st; + + CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStart - id: %d, type: %s", index, type); + + if(!strcmp(type, "AC3")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_AC3; + } else if(!strcmp(type, "EAC3")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_EAC3; + } else if(!strcmp(type, "MPEG2AUDIO")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_MP2; + } else if(!strcmp(type, "AAC")) { + st.a = new CDemuxStreamAudioHTSP(this, type); + st.a->codec = AV_CODEC_ID_AAC; + } else if(!strcmp(type, "MPEG2VIDEO")) { + st.v = new CDemuxStreamVideoHTSP(this, type); + st.v->codec = AV_CODEC_ID_MPEG2VIDEO; + st.v->iWidth = htsmsg_get_u32_or_default(sub, "width" , 0); + st.v->iHeight = htsmsg_get_u32_or_default(sub, "height", 0); + } else if(!strcmp(type, "H264")) { + st.v = new CDemuxStreamVideoHTSP(this, type); + st.v->codec = AV_CODEC_ID_H264; + st.v->iWidth = htsmsg_get_u32_or_default(sub, "width" , 0); + st.v->iHeight = htsmsg_get_u32_or_default(sub, "height", 0); + } else if(!strcmp(type, "DVBSUB")) { + st.s = new CDemuxStreamSubtitle(); + st.s->codec = AV_CODEC_ID_DVB_SUBTITLE; + uint32_t composition_id = 0, ancillary_id = 0; + htsmsg_get_u32(sub, "composition_id", &composition_id); + htsmsg_get_u32(sub, "ancillary_id" , &ancillary_id); + if(composition_id || ancillary_id) + { + st.s->ExtraData = new uint8_t[4]; + st.s->ExtraSize = 4; + st.s->ExtraData[0] = (composition_id >> 8) & 0xff; + st.s->ExtraData[1] = (composition_id >> 0) & 0xff; + st.s->ExtraData[2] = (ancillary_id >> 8) & 0xff; + st.s->ExtraData[3] = (ancillary_id >> 0) & 0xff; + } + } else if(!strcmp(type, "TEXTSUB")) { + st.s = new CDemuxStreamSubtitle(); + st.s->codec = AV_CODEC_ID_TEXT; + } else if(!strcmp(type, "TELETEXT")) { + st.t = new CDemuxStreamTeletext(); + st.t->codec = AV_CODEC_ID_DVB_TELETEXT; + } else { + continue; + } + + if((lang = htsmsg_get_str(sub, "language"))) + { + strncpy(st.g->language, lang, sizeof(st.g->language)); + st.g->language[sizeof(st.g->language) - 1] = '\0'; + } + + st.g->iId = m_Streams.size(); + st.g->iPhysicalId = index; + m_Streams.push_back(st.g); + } +} +void CDVDDemuxHTSP::SubscriptionStop (htsmsg_t *m) +{ + for(int i = 0; i < (int)m_Streams.size(); i++) + delete m_Streams[i]; + m_Streams.clear(); +} + +void CDVDDemuxHTSP::SubscriptionStatus(htsmsg_t *m) +{ + const char* status; + status = htsmsg_get_str(m, "status"); + if(status == NULL) + m_Status = ""; + else + { + m_StatusCount++; + m_Status = status; + CLog::Log(LOGDEBUG, "CDVDDemuxHTSP::SubscriptionStatus - %s", status); + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, "TVHeadend Status", status, TOAST_DISPLAY_TIME, false); + } +} + +CDemuxStream* CDVDDemuxHTSP::GetStream(int iStreamId) +{ + if(iStreamId >= 0 && iStreamId < (int)m_Streams.size()) + return m_Streams[iStreamId]; + + return NULL; +} + +int CDVDDemuxHTSP::GetNrOfStreams() +{ + return m_Streams.size(); +} + +std::string CDVDDemuxHTSP::GetFileName() +{ + if(m_Input) + return m_Input->GetFileName(); + else + return ""; +} + +void CDVDDemuxHTSP::Abort() +{ + if(m_Input) + return m_Input->Abort(); +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h new file mode 100644 index 0000000..6d73a9d --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxHTSP.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once +#include "DVDDemux.h" +#include "filesystem/HTSPSession.h" + +class CDVDInputStreamHTSP; +typedef struct htsmsg htsmsg_t; + +class CDVDDemuxHTSP : public CDVDDemux +{ +public: + CDVDDemuxHTSP(); + virtual ~CDVDDemuxHTSP(); + + bool Open(CDVDInputStream* input); + void Dispose(); + void Reset(); + void Flush(); + void Abort(); + void SetSpeed(int iSpeed){}; + + std::string GetFileName(); + + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL) { return false; } + int GetStreamLength() { return 0; } + + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + +protected: + friend class CDemuxStreamVideoHTSP; + + void SubscriptionStart (htsmsg_t *m); + void SubscriptionStop (htsmsg_t *m); + void SubscriptionStatus(htsmsg_t *m); + + htsmsg_t* ReadStream(); + bool ReadStream(uint8_t* buf, int len); + + typedef std::vector TStreams; + + CDVDInputStream* m_Input; + TStreams m_Streams; + std::string m_Status; + int m_StatusCount; + HTSP::SQueueStatus m_QueueStatus; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp new file mode 100644 index 0000000..4ed2d5c --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDDemuxPVRClient.h" +#include "DVDDemuxUtils.h" +#include "utils/log.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" +#include "../DVDClock.h" + +#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - FF_INPUT_BUFFER_PADDING_SIZE) + +using namespace PVR; + +CDemuxStreamPVRInternal::CDemuxStreamPVRInternal(CDVDDemuxPVRClient *parent) + : m_parent(parent) + , m_parser(NULL) + , m_context(NULL) + , m_parser_split(false) +{ +} + +CDemuxStreamPVRInternal::~CDemuxStreamPVRInternal() +{ + DisposeParser(); +} + +void CDemuxStreamPVRInternal::DisposeParser() +{ + if (m_parser) + { + av_parser_close(m_parser); + m_parser = NULL; + } + if (m_context) + { + avcodec_close(m_context); + m_context = NULL; + } +} + +void CDemuxStreamVideoPVRClient::GetStreamInfo(std::string& strInfo) +{ + switch (codec) + { + case AV_CODEC_ID_MPEG2VIDEO: + strInfo = "mpeg2video"; + break; + case AV_CODEC_ID_H264: + strInfo = "h264"; + break; + default: + break; + } +} + +void CDemuxStreamAudioPVRClient::GetStreamInfo(std::string& strInfo) +{ + switch (codec) + { + case AV_CODEC_ID_AC3: + strInfo = "ac3"; + break; + case AV_CODEC_ID_EAC3: + strInfo = "eac3"; + break; + case AV_CODEC_ID_MP2: + strInfo = "mpeg2audio"; + break; + case AV_CODEC_ID_AAC: + strInfo = "aac"; + break; + case AV_CODEC_ID_DTS: + strInfo = "dts"; + break; + default: + break; + } +} + +void CDemuxStreamSubtitlePVRClient::GetStreamInfo(std::string& strInfo) +{ +} + +CDVDDemuxPVRClient::CDVDDemuxPVRClient() : CDVDDemux() +{ + m_pInput = NULL; + for (int i = 0; i < MAX_STREAMS; i++) m_streams[i] = NULL; +} + +CDVDDemuxPVRClient::~CDVDDemuxPVRClient() +{ + Dispose(); +} + +bool CDVDDemuxPVRClient::Open(CDVDInputStream* pInput) +{ + Abort(); + + m_pInput = pInput; + if (!g_PVRClients->GetPlayingClient(m_pvrClient)) + return false; + + return true; +} + +void CDVDDemuxPVRClient::Dispose() +{ + for (int i = 0; i < MAX_STREAMS; i++) + { + delete m_streams[i]; + m_streams[i] = NULL; + } + + m_pInput = NULL; +} + +void CDVDDemuxPVRClient::DisposeStream(int iStreamId) +{ + if (iStreamId < 0 || iStreamId >= MAX_STREAMS) + return; + delete m_streams[iStreamId]; + m_streams[iStreamId] = NULL; +} + +void CDVDDemuxPVRClient::Reset() +{ + if(m_pInput && g_PVRManager.IsStarted()) + m_pvrClient->DemuxReset(); + + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxPVRClient::Abort() +{ + if(m_pInput) + m_pvrClient->DemuxAbort(); +} + +void CDVDDemuxPVRClient::Flush() +{ + if(m_pInput && g_PVRManager.IsStarted()) + m_pvrClient->DemuxFlush(); +} + +void CDVDDemuxPVRClient::ParsePacket(DemuxPacket* pkt) +{ + CDemuxStream* st = m_streams[pkt->iStreamId]; + if (st == NULL) + return; + + if (st->ExtraSize) + return; + + CDemuxStreamPVRInternal* pvr = dynamic_cast(st); + + if(pvr == NULL + || pvr->m_parser == NULL) + return; + + if(pvr->m_context == NULL) + { + AVCodec *codec = avcodec_find_decoder(st->codec); + if (codec == NULL) + { + CLog::Log(LOGERROR, "%s - can't find decoder", __FUNCTION__); + pvr->DisposeParser(); + return; + } + + pvr->m_context = avcodec_alloc_context3(codec); + if(pvr->m_context == NULL) + { + CLog::Log(LOGERROR, "%s - can't allocate context", __FUNCTION__); + pvr->DisposeParser(); + return; + } + pvr->m_context->time_base.num = 1; + pvr->m_context->time_base.den = DVD_TIME_BASE; + } + + if(pvr->m_parser_split && pvr->m_parser->parser->split) + { + int len = pvr->m_parser->parser->split(pvr->m_context, pkt->pData, pkt->iSize); + if (len > 0 && len < FF_MAX_EXTRADATA_SIZE) + { + if (st->ExtraData) + delete[] (uint8_t*)st->ExtraData; + st->changes++; + st->disabled = false; + st->ExtraSize = len; + st->ExtraData = new uint8_t[len+FF_INPUT_BUFFER_PADDING_SIZE]; + memcpy(st->ExtraData, pkt->pData, len); + memset((uint8_t*)st->ExtraData + len, 0 , FF_INPUT_BUFFER_PADDING_SIZE); + pvr->m_parser_split = false; + } + } + + + uint8_t *outbuf = NULL; + int outbuf_size = 0; + int len = av_parser_parse2(pvr->m_parser + , pvr->m_context, &outbuf, &outbuf_size + , pkt->pData, pkt->iSize + , (int64_t)(pkt->pts * DVD_TIME_BASE) + , (int64_t)(pkt->dts * DVD_TIME_BASE) + , 0); + /* our parse is setup to parse complete frames, so we don't care about outbufs */ + if(len >= 0) + { +#define CHECK_UPDATE(st, trg, src, invalid) do { \ + if(src != invalid \ + && src != st->trg) { \ + CLog::Log(LOGDEBUG, "%s - {%d} " #trg " changed from %d to %d", __FUNCTION__, st->iId, st->trg, src); \ + st->trg = src; \ + st->changes++; \ + st->disabled = false; \ + } \ + } while(0) + + + CHECK_UPDATE(st, profile, pvr->m_context->profile , FF_PROFILE_UNKNOWN); + CHECK_UPDATE(st, level , pvr->m_context->level , FF_LEVEL_UNKNOWN); + + switch (st->type) + { + case STREAM_AUDIO: { + CDemuxStreamAudioPVRClient* sta = static_cast(st); + CHECK_UPDATE(sta, iChannels , pvr->m_context->channels , 0); + CHECK_UPDATE(sta, iSampleRate , pvr->m_context->sample_rate, 0); + break; + } + case STREAM_VIDEO: { + CDemuxStreamVideoPVRClient* stv = static_cast(st); + CHECK_UPDATE(stv, iWidth , pvr->m_context->width , 0); + CHECK_UPDATE(stv, iHeight , pvr->m_context->height, 0); + break; + } + + default: + break; + } + +#undef CHECK_UPDATE + } + else + CLog::Log(LOGDEBUG, "%s - parser returned error %d", __FUNCTION__, len); + + return; +} + +DemuxPacket* CDVDDemuxPVRClient::Read() +{ + if (!g_PVRManager.IsStarted()) + return CDVDDemuxUtils::AllocateDemuxPacket(0); + + DemuxPacket* pPacket = m_pvrClient->DemuxRead(); + if (!pPacket) + { + if (m_pInput) + m_pInput->Close(); + return NULL; + } + + if (pPacket->iStreamId == DMX_SPECIALID_STREAMINFO) + { + RequestStreams(); + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + return CDVDDemuxUtils::AllocateDemuxPacket(0); + } + else if (pPacket->iStreamId == DMX_SPECIALID_STREAMCHANGE) + { + RequestStreams(); + } + else if (pPacket->iStreamId >= 0 + && pPacket->iStreamId < MAX_STREAMS + && m_streams[pPacket->iStreamId]) + { + ParsePacket(pPacket); + } + + return pPacket; +} + +CDemuxStream* CDVDDemuxPVRClient::GetStream(int iStreamId) +{ + if (iStreamId < 0 || iStreamId >= MAX_STREAMS) return NULL; + return m_streams[iStreamId]; +} + +void CDVDDemuxPVRClient::RequestStreams() +{ + if (!g_PVRManager.IsStarted()) + return; + + PVR_STREAM_PROPERTIES props = {}; + m_pvrClient->GetStreamProperties(&props); + unsigned int i; + + for (i = 0; i < props.iStreamCount; ++i) + { + CDemuxStream *stm = m_streams[i]; + + if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_AUDIO) + { + CDemuxStreamAudioPVRClient* st = NULL; + if (stm) + { + st = dynamic_cast(stm); + if (!st || (st->codec != (AVCodecID)props.stream[i].iCodecId)) + DisposeStream(i); + } + if (!m_streams[i]) + { + st = new CDemuxStreamAudioPVRClient(this); + st->m_parser = av_parser_init(props.stream[i].iCodecId); + if(st->m_parser) + st->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } + st->iChannels = props.stream[i].iChannels; + st->iSampleRate = props.stream[i].iSampleRate; + st->iBlockAlign = props.stream[i].iBlockAlign; + st->iBitRate = props.stream[i].iBitRate; + st->iBitsPerSample = props.stream[i].iBitsPerSample; + m_streams[i] = st; + st->m_parser_split = true; + st->changes++; + } + else if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_VIDEO) + { + CDemuxStreamVideoPVRClient* st = NULL; + if (stm) + { + st = dynamic_cast(stm); + if (!st + || (st->codec != (AVCodecID)props.stream[i].iCodecId) + || (st->iWidth != props.stream[i].iWidth) + || (st->iHeight != props.stream[i].iHeight)) + DisposeStream(i); + } + if (!m_streams[i]) + { + st = new CDemuxStreamVideoPVRClient(this); + st->m_parser = av_parser_init(props.stream[i].iCodecId); + if(st->m_parser) + st->m_parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + } + st->iFpsScale = props.stream[i].iFPSScale; + st->iFpsRate = props.stream[i].iFPSRate; + st->iHeight = props.stream[i].iHeight; + st->iWidth = props.stream[i].iWidth; + st->fAspect = props.stream[i].fAspect; + st->stereo_mode = "mono"; + m_streams[i] = st; + st->m_parser_split = true; + } + else if (props.stream[i].iCodecId == AV_CODEC_ID_DVB_TELETEXT) + { + if (stm) + { + if (stm->codec != (AVCodecID)props.stream[i].iCodecId) + DisposeStream(i); + } + if (!m_streams[i]) + m_streams[i] = new CDemuxStreamTeletext(); + } + else if (props.stream[i].iCodecType == XBMC_CODEC_TYPE_SUBTITLE) + { + CDemuxStreamSubtitlePVRClient* st = NULL; + if (stm) + { + st = dynamic_cast(stm); + if (!st || (st->codec != (AVCodecID)props.stream[i].iCodecId)) + DisposeStream(i); + } + if (!m_streams[i]) + { + st = new CDemuxStreamSubtitlePVRClient(this); + } + if(props.stream[i].iIdentifier) + { + st->ExtraData = new uint8_t[4]; + st->ExtraSize = 4; + st->ExtraData[0] = (props.stream[i].iIdentifier >> 8) & 0xff; + st->ExtraData[1] = (props.stream[i].iIdentifier >> 0) & 0xff; + st->ExtraData[2] = (props.stream[i].iIdentifier >> 24) & 0xff; + st->ExtraData[3] = (props.stream[i].iIdentifier >> 16) & 0xff; + } + m_streams[i] = st; + } + else + { + if (m_streams[i]) + DisposeStream(i); + m_streams[i] = new CDemuxStream(); + } + + m_streams[i]->codec = (AVCodecID)props.stream[i].iCodecId; + m_streams[i]->iId = i; + m_streams[i]->iPhysicalId = props.stream[i].iPhysicalId; + m_streams[i]->language[0] = props.stream[i].strLanguage[0]; + m_streams[i]->language[1] = props.stream[i].strLanguage[1]; + m_streams[i]->language[2] = props.stream[i].strLanguage[2]; + m_streams[i]->language[3] = props.stream[i].strLanguage[3]; + + CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): added/updated stream %d:%d with codec_id %d", + m_streams[i]->iId, + m_streams[i]->iPhysicalId, + m_streams[i]->codec); + } + // check if we need to dispose any streams no longer in props + for (unsigned int j = i; j < MAX_STREAMS; j++) + { + if (m_streams[j]) + { + CLog::Log(LOGDEBUG,"CDVDDemuxPVRClient::RequestStreams(): disposed stream %d:%d with codec_id %d", + m_streams[j]->iId, + m_streams[j]->iPhysicalId, + m_streams[j]->codec); + DisposeStream(j); + } + } +} + +int CDVDDemuxPVRClient::GetNrOfStreams() +{ + int i = 0; + while (i < MAX_STREAMS && m_streams[i]) i++; + return i; +} + +std::string CDVDDemuxPVRClient::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + +void CDVDDemuxPVRClient::GetStreamCodecName(int iStreamId, std::string &strName) +{ + CDemuxStream *stream = GetStream(iStreamId); + if (stream) + { + if (stream->codec == AV_CODEC_ID_AC3) + strName = "ac3"; + else if (stream->codec == AV_CODEC_ID_MP2) + strName = "mp2"; + else if (stream->codec == AV_CODEC_ID_AAC) + strName = "aac"; + else if (stream->codec == AV_CODEC_ID_DTS) + strName = "dca"; + else if (stream->codec == AV_CODEC_ID_MPEG2VIDEO) + strName = "mpeg2video"; + else if (stream->codec == AV_CODEC_ID_H264) + strName = "h264"; + else if (stream->codec == AV_CODEC_ID_EAC3) + strName = "eac3"; + } +} + +bool CDVDDemuxPVRClient::SeekTime(int timems, bool backwards, double *startpts) +{ + if (m_pInput) + return m_pvrClient->SeekTime(timems, backwards, startpts); + return false; +} + +void CDVDDemuxPVRClient::SetSpeed ( int speed ) +{ + if (m_pInput) + m_pvrClient->SetSpeed(speed); +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h new file mode 100644 index 0000000..2fc3c63 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPVRClient.h @@ -0,0 +1,118 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" +#include +#include "pvr/addons/PVRClient.h" + +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +} + +class CDVDDemuxPVRClient; +struct PVR_STREAM_PROPERTIES; + +class CDemuxStreamPVRInternal +{ +public: + CDemuxStreamPVRInternal(CDVDDemuxPVRClient *parent); + ~CDemuxStreamPVRInternal(); + + void DisposeParser(); + + CDVDDemuxPVRClient * m_parent; + AVCodecParserContext* m_parser; + AVCodecContext * m_context; + bool m_parser_split; +}; + +class CDemuxStreamVideoPVRClient + : public CDemuxStreamVideo + , public CDemuxStreamPVRInternal +{ +public: + CDemuxStreamVideoPVRClient(CDVDDemuxPVRClient *parent) + : CDemuxStreamPVRInternal(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDemuxStreamAudioPVRClient + : public CDemuxStreamAudio + , public CDemuxStreamPVRInternal +{ +public: + CDemuxStreamAudioPVRClient(CDVDDemuxPVRClient *parent) + : CDemuxStreamPVRInternal(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + +class CDemuxStreamSubtitlePVRClient + : public CDemuxStreamSubtitle + , public CDemuxStreamPVRInternal +{ +public: + CDemuxStreamSubtitlePVRClient(CDVDDemuxPVRClient *parent) + : CDemuxStreamPVRInternal(parent) + {} + virtual void GetStreamInfo(std::string& strInfo); +}; + + +class CDVDDemuxPVRClient : public CDVDDemux +{ + friend class CDemuxStreamPVRInternal; + +public: + + CDVDDemuxPVRClient(); + ~CDVDDemuxPVRClient(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Abort(); + void Flush(); + DemuxPacket* Read(); + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + void SetSpeed(int iSpeed); + int GetStreamLength() { return 0; } + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + std::string GetFileName(); + virtual void GetStreamCodecName(int iStreamId, std::string &strName); + +protected: + CDVDInputStream* m_pInput; +#ifndef MAX_STREAMS + #define MAX_STREAMS 100 +#endif + CDemuxStream* m_streams[MAX_STREAMS]; // maximum number of streams that ffmpeg can handle + std::shared_ptr m_pvrClient; + +private: + void RequestStreams(); + void ParsePacket(DemuxPacket* pPacket); + void DisposeStream(int iStreamId); +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h new file mode 100644 index 0000000..d64fbb3 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h @@ -0,0 +1,36 @@ +#pragma once + +/* + * Copyright (C) 2012-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#define DMX_SPECIALID_STREAMINFO -10 +#define DMX_SPECIALID_STREAMCHANGE -11 + + typedef struct DemuxPacket +{ + unsigned char* pData; // data + int iSize; // data size + int iStreamId; // integer representing the stream index + int iGroupId; // the group this data belongs to, used to group data from different streams together + + double pts; // pts in DVD_TIME_BASE + double dts; // dts in DVD_TIME_BASE + double duration; // duration in DVD_TIME_BASE if available +} DemuxPacket; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp new file mode 100644 index 0000000..a69342a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDCodecs/DVDCodecs.h" +#include "DVDInputStreams/DVDInputStreamHttp.h" +#include "DVDDemuxShoutcast.h" +#include "DVDDemuxUtils.h" +#include "DVDClock.h" // for DVD_TIME_BASE +#include "../../../utils/HttpHeader.h" + +#define ICY_NOTICE1 "icy-notice1" // string +#define ICY_NOTICE2 "icy-notice2" // string +#define ICY_NAME "icy-name" // string +#define ICY_GENRE "icy-genre" // string +#define ICY_URL "icy-url" // string +#define ICY_PUBLIC "icy-pub" // int (1 / 0) +#define ICY_BITRATE "icy-br" // int (bitrate = val * 1000 ?) +#define ICY_METAINTERVAL "icy-metaint" // int + +#define CONTENT_TYPE_MP3 "audio/mpeg" +#define CONTENT_TYPE_AAC "audio/aac" +#define CONTENT_TYPE_AACPLUS "audio/aacp" + +// class CDemuxStreamVideoFFmpeg +void CDemuxStreamAudioShoutcast::GetStreamInfo(std::string& strInfo) +{ + strInfo = "Shoutcast"; +} + +CDVDDemuxShoutcast::CDVDDemuxShoutcast() : CDVDDemux() +{ + m_pInput = NULL; + m_pDemuxStream = NULL; + m_iMetaStreamInterval = 0; +} + +CDVDDemuxShoutcast::~CDVDDemuxShoutcast() +{ + Dispose(); +} + +bool CDVDDemuxShoutcast::Open(CDVDInputStream* pInput) +{ + Dispose(); + + m_pInput = pInput; + + // the input stream should be a http stream + if (!pInput->IsStreamType(DVDSTREAM_TYPE_HTTP)) return false; + CDVDInputStreamHttp* pInputStreamHttp = (CDVDInputStreamHttp*)pInput; + + CHttpHeader* pHeader = pInputStreamHttp->GetHttpHeader(); + + std::string strMetaInt = pHeader->GetValue(ICY_METAINTERVAL); + std::string strMimeType = pHeader->GetMimeType(); + + // create new demuxer stream + m_pDemuxStream = new CDemuxStreamAudioShoutcast(); + m_pDemuxStream->iId = 0; + m_pDemuxStream->iPhysicalId = 0; + m_pDemuxStream->iDuration = 0; + m_pDemuxStream->iChannels = 2; + m_pDemuxStream->iSampleRate = 0; + + // set meta interval + m_iMetaStreamInterval = atoi(strMetaInt.c_str()); + + if (stricmp(strMimeType.c_str(), CONTENT_TYPE_AAC) == 0 || + stricmp(strMimeType.c_str(), CONTENT_TYPE_AACPLUS) == 0) + { + // need an aac decoder first + m_pDemuxStream->codec = AV_CODEC_ID_AAC; + } + else // (stricmp(strMimeType, CONTENT_TYPE_MP3) == 0) + { + // default to mp3 + m_pDemuxStream->codec = AV_CODEC_ID_MP3; + } + + return true; +} + +void CDVDDemuxShoutcast::Dispose() +{ + if (m_pDemuxStream) delete m_pDemuxStream; + m_pDemuxStream = NULL; + + m_pInput = NULL; +} + +void CDVDDemuxShoutcast::Reset() +{ + CDVDInputStream* pInputStream = m_pInput; + Dispose(); + Open(pInputStream); +} + +void CDVDDemuxShoutcast::Flush() +{ +} + +DemuxPacket* CDVDDemuxShoutcast::Read() +{ + // XXX + // if meta interval is greater than FileCurl's max read size (currently 64k) + // it will simply fail becuse the meta-interval will get incorrect + + int iDataToRead = SHOUTCAST_BUFFER_SIZE; + if (m_iMetaStreamInterval > 0) iDataToRead = m_iMetaStreamInterval; + + DemuxPacket* pPacket; + pPacket = CDVDDemuxUtils::AllocateDemuxPacket(iDataToRead); + if (pPacket) + { + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + pPacket->iStreamId = 0; + + // read the data + int iRead = m_pInput->Read(pPacket->pData, iDataToRead); + + pPacket->iSize = iRead; + + if (iRead <= 0) + { + CDVDDemuxUtils::FreeDemuxPacket(pPacket); + pPacket = NULL; + } + } + + if (m_iMetaStreamInterval > 0) + { + // we already have read m_iMetaStreamInterval bytes of streaming data + // metadata follows + uint8_t l; + int iRead = m_pInput->Read(&l, 1); + if (iRead > 0) + { + int iMetaLength = l * 16; + + if (iMetaLength > 0) + { + // iMetaLength cannot be larger then 16 * 255 + uint8_t buffer[16 * 255]; + + // skip meta data for now + m_pInput->Read(buffer, iMetaLength); + } + } + } + + return pPacket; +} + +bool CDVDDemuxShoutcast::SeekTime(int time, bool backwords, double* startpts) +{ + return false; +} + +int CDVDDemuxShoutcast::GetStreamLength() +{ + return 0; +} + +CDemuxStream* CDVDDemuxShoutcast::GetStream(int iStreamId) +{ + return m_pDemuxStream; +} + +int CDVDDemuxShoutcast::GetNrOfStreams() +{ + return 1; +} + +std::string CDVDDemuxShoutcast::GetFileName() +{ + if(m_pInput) + return m_pInput->GetFileName(); + else + return ""; +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h new file mode 100644 index 0000000..a802167 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxShoutcast.h @@ -0,0 +1,60 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" + +class CDemuxStreamAudioShoutcast : public CDemuxStreamAudio +{ +public: + virtual void GetStreamInfo(std::string& strInfo); +}; + +#define SHOUTCAST_BUFFER_SIZE 1024 * 32 + +class CDVDDemuxShoutcast : public CDVDDemux +{ +public: + CDVDDemuxShoutcast(); + virtual ~CDVDDemuxShoutcast(); + + bool Open(CDVDInputStream* pInput); + void Dispose(); + void Reset(); + void Flush(); + void Abort(){} + void SetSpeed(int iSpeed){}; + virtual std::string GetFileName(); + + DemuxPacket* Read(); + + bool SeekTime(int time, bool backwords = false, double* startpts = NULL); + int GetStreamLength(); + CDemuxStream* GetStream(int iStreamId); + int GetNrOfStreams(); + +protected: + + CDemuxStreamAudioShoutcast* m_pDemuxStream; + + int m_iMetaStreamInterval; + CDVDInputStream* m_pInput; +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp new file mode 100644 index 0000000..ab298b2 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#if (defined HAVE_CONFIG_H) && (!defined TARGET_WINDOWS) + #include "config.h" +#endif +#include "DVDDemuxUtils.h" +#include "DVDClock.h" +#include "utils/log.h" + +extern "C" { +#include "libavcodec/avcodec.h" +} + +void CDVDDemuxUtils::FreeDemuxPacket(DemuxPacket* pPacket) +{ + if (pPacket) + { + try { + if (pPacket->pData) _aligned_free(pPacket->pData); + delete pPacket; + } + catch(...) { + CLog::Log(LOGERROR, "%s - Exception thrown while freeing packet", __FUNCTION__); + } + } +} + +DemuxPacket* CDVDDemuxUtils::AllocateDemuxPacket(int iDataSize) +{ + DemuxPacket* pPacket = new DemuxPacket; + if (!pPacket) return NULL; + + try + { + memset(pPacket, 0, sizeof(DemuxPacket)); + + if (iDataSize > 0) + { + // need to allocate a few bytes more. + // From avcodec.h (ffmpeg) + /** + * Required number of additionally allocated bytes at the end of the input bitstream for decoding. + * this is mainly needed because some optimized bitstream readers read + * 32 or 64 bit at once and could read over the end
+ * Note, if the first 23 bits of the additional bytes are not 0 then damaged + * MPEG bitstreams could cause overread and segfault + */ + pPacket->pData =(uint8_t*)_aligned_malloc(iDataSize + FF_INPUT_BUFFER_PADDING_SIZE, 16); + if (!pPacket->pData) + { + FreeDemuxPacket(pPacket); + return NULL; + } + + // reset the last 8 bytes to 0; + memset(pPacket->pData + iDataSize, 0, FF_INPUT_BUFFER_PADDING_SIZE); + } + + // setup defaults + pPacket->dts = DVD_NOPTS_VALUE; + pPacket->pts = DVD_NOPTS_VALUE; + pPacket->iStreamId = -1; + } + catch(...) + { + CLog::Log(LOGERROR, "%s - Exception thrown", __FUNCTION__); + FreeDemuxPacket(pPacket); + pPacket = NULL; + } + return pPacket; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h new file mode 100644 index 0000000..2c12df3 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h @@ -0,0 +1,31 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxPacket.h" + +class CDVDDemuxUtils +{ +public: + static void FreeDemuxPacket(DemuxPacket* pPacket); + static DemuxPacket* AllocateDemuxPacket(int iDataSize = 0); +}; + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp new file mode 100644 index 0000000..9625a19 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemuxVobsub.h" +#include "DVDInputStreams/DVDFactoryInputStream.h" +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDStreamInfo.h" +#include "DVDCodecs/DVDCodecs.h" +#include "DVDDemuxers/DVDDemuxFFmpeg.h" +#include "DVDDemuxers/DVDDemuxUtils.h" +#include "DVDClock.h" +#include "DVDSubtitles/DVDSubtitleStream.h" + +#include + +using namespace std; + +CDVDDemuxVobsub::CDVDDemuxVobsub() +{ +} + +CDVDDemuxVobsub::~CDVDDemuxVobsub() +{ + for(unsigned i=0;i pStream(new CDVDSubtitleStream()); + if(!pStream->Open(filename)) + return false; + + string vobsub = subfilename; + if ( vobsub == "") + { + vobsub = filename; + vobsub.erase(vobsub.rfind('.'), vobsub.size()); + vobsub += ".sub"; + } + + m_Input.reset(CDVDFactoryInputStream::CreateInputStream(NULL, vobsub, "")); + if(!m_Input.get() || !m_Input->Open(vobsub.c_str(), "video/x-vobsub")) + return false; + + m_Demuxer.reset(new CDVDDemuxFFmpeg()); + if(!m_Demuxer->Open(m_Input.get())) + return false; + + CDVDStreamInfo hints; + CDVDCodecOptions options; + hints.codec = AV_CODEC_ID_DVD_SUBTITLE; + + char line[2048]; + DECLARE_UNUSED(bool,res) + + SState state; + state.delay = 0; + state.id = -1; + + while( pStream->ReadLine(line, sizeof(line)) ) + { + if (*line == 0 || *line == '\r' || *line == '\n' || *line == '#') + continue; + else if (strncmp("langidx:", line, 8) == 0) + res = ParseLangIdx(state, line + 8); + else if (strncmp("delay:", line, 6) == 0) + res = ParseDelay(state, line + 6); + else if (strncmp("id:", line, 3) == 0) + res = ParseId(state, line + 3); + else if (strncmp("timestamp:", line, 10) == 0) + res = ParseTimestamp(state, line + 10); + else if (strncmp("palette:", line, 8) == 0 + || strncmp("size:", line, 5) == 0 + || strncmp("org:", line, 4) == 0 + || strncmp("custom colors:", line, 14) == 0 + || strncmp("scale:", line, 6) == 0 + || strncmp("alpha:", line, 6) == 0 + || strncmp("fadein/out:", line, 11) == 0 + || strncmp("forced subs:", line, 12) == 0) + res = ParseExtra(state, line); + else + continue; + } + + struct sorter s; + sort(m_Timestamps.begin(), m_Timestamps.end(), s); + m_Timestamp = m_Timestamps.begin(); + + for(unsigned i=0;iExtraSize = state.extra.length()+1; + m_Streams[i]->ExtraData = new uint8_t[m_Streams[i]->ExtraSize]; + strcpy((char*)m_Streams[i]->ExtraData, state.extra.c_str()); + } + + return true; +} + +void CDVDDemuxVobsub::Reset() +{ + Flush(); +} + +void CDVDDemuxVobsub::Flush() +{ + m_Demuxer->Flush(); +} + +bool CDVDDemuxVobsub::SeekTime(int time, bool backwords, double* startpts) +{ + double pts = DVD_MSEC_TO_TIME(time); + m_Timestamp = m_Timestamps.begin(); + for(;m_Timestamp != m_Timestamps.end();++m_Timestamp) + { + if(m_Timestamp->pts > pts) + break; + } + for(unsigned i=0;i::iterator current; + do { + if(m_Timestamp == m_Timestamps.end()) + return NULL; + + current = m_Timestamp++; + } while(m_Streams[current->id]->m_discard == AVDISCARD_ALL); + + if(!m_Demuxer->SeekByte(current->pos)) + return NULL; + + DemuxPacket *packet = m_Demuxer->Read(); + if(!packet) + return NULL; + + packet->iStreamId = current->id; + packet->pts = current->pts; + packet->dts = current->pts; + + return packet; +} + +bool CDVDDemuxVobsub::ParseLangIdx(SState& state, char* line) +{ + return true; +} + +bool CDVDDemuxVobsub::ParseDelay(SState& state, char* line) +{ + int h,m,s,ms; + bool negative = false; + + while(*line == ' ') line++; + if(*line == '-') + { + line++; + negative = true; + } + if(sscanf(line, "%d:%d:%d:%d", &h, &m, &s, &ms) != 4) + return false; + state.delay = h*3600.0 + m*60.0 + s + ms*0.001; + if(negative) + state.delay *= -1; + return true; +} + +bool CDVDDemuxVobsub::ParseId(SState& state, char* line) +{ + unique_ptr stream(new CStream(this)); + + while(*line == ' ') line++; + strncpy(stream->language, line, 2); + stream->language[2] = '\0'; + line+=2; + + while(*line == ' ' || *line == ',') line++; + if (strncmp("index:", line, 6) == 0) + { + line+=6; + while(*line == ' ') line++; + stream->iPhysicalId = atoi(line); + } + else + stream->iPhysicalId = -1; + + stream->codec = AV_CODEC_ID_DVD_SUBTITLE; + stream->iId = m_Streams.size(); + stream->source = STREAM_SOURCE_DEMUX_SUB; + + state.id = stream->iId; + m_Streams.push_back(stream.release()); + return true; +} + +bool CDVDDemuxVobsub::ParseExtra(SState& state, char* line) +{ + state.extra += line; + state.extra += '\n'; + return true; +} + +bool CDVDDemuxVobsub::ParseTimestamp(SState& state, char* line) +{ + if(state.id < 0) + return false; + + int h,m,s,ms; + STimestamp timestamp; + + while(*line == ' ') line++; + if(sscanf(line, "%d:%d:%d:%d, filepos:%" PRIx64, &h, &m, &s, &ms, ×tamp.pos) != 5) + return false; + + timestamp.id = state.id; + timestamp.pts = DVD_SEC_TO_TIME(state.delay + h*3600.0 + m*60.0 + s + ms*0.001); + m_Timestamps.push_back(timestamp); + return true; +} diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h new file mode 100644 index 0000000..0c75c4a --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxVobsub.h @@ -0,0 +1,99 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "DVDDemux.h" + +#include +#include + +class CDVDOverlayCodecFFmpeg; +class CDVDInputStream; +class CDVDDemuxFFmpeg; + +class CDVDDemuxVobsub : public CDVDDemux +{ +public: + CDVDDemuxVobsub(); + virtual ~CDVDDemuxVobsub(); + + virtual bool Open(const std::string& filename, const std::string& subfilename = ""); + virtual void Reset(); + virtual void Abort() {}; + virtual void Flush(); + virtual DemuxPacket* Read(); + virtual bool SeekTime(int time, bool backwords, double* startpts = NULL); + virtual void SetSpeed(int speed) {} + virtual CDemuxStream* GetStream(int index) { return m_Streams[index]; } + virtual int GetNrOfStreams() { return m_Streams.size(); } + virtual int GetStreamLength() { return 0; } + virtual std::string GetFileName() { return m_Filename; } + +private: + class CStream + : public CDemuxStreamSubtitle + { + public: + CStream(CDVDDemuxVobsub* parent) + : m_discard(AVDISCARD_NONE), m_parent(parent) + {} + virtual void SetDiscard(AVDiscard discard) { m_discard = discard; } + virtual AVDiscard GetDiscard() { return m_discard; } + + AVDiscard m_discard; + CDVDDemuxVobsub* m_parent; + }; + + typedef struct STimestamp + { + int64_t pos; + double pts; + int id; + } STimestamp; + + std::string m_Filename; + std::unique_ptr m_Input; + std::unique_ptr m_Demuxer; + std::vector m_Timestamps; + std::vector::iterator m_Timestamp; + std::vector m_Streams; + + typedef struct SState + { + int id; + double delay; + std::string extra; + } SState; + + struct sorter + { + bool operator()(const STimestamp &p1, const STimestamp &p2) + { + return p1.pts < p2.pts || (p1.pts == p2.pts && p1.id < p2.id); + } + }; + + bool ParseLangIdx(SState& state, char* line); + bool ParseDelay(SState& state, char* line); + bool ParseId(SState& state, char* line); + bool ParseExtra(SState& state, char* line); + bool ParseTimestamp(SState& state, char* line); +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp new file mode 100644 index 0000000..f909c32 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "system.h" +#include "DVDFactoryDemuxer.h" + +#include "DVDInputStreams/DVDInputStream.h" +#include "DVDInputStreams/DVDInputStreamHttp.h" +#include "DVDInputStreams/DVDInputStreamPVRManager.h" + +#include "DVDDemuxFFmpeg.h" +#include "DVDDemuxShoutcast.h" +#ifdef HAS_FILESYSTEM_HTSP +#include "DVDDemuxHTSP.h" +#endif +#include "DVDDemuxBXA.h" +#include "DVDDemuxCDDA.h" +#include "DVDDemuxPVRClient.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClients.h" + +using namespace std; +using namespace PVR; + +CDVDDemux* CDVDFactoryDemuxer::CreateDemuxer(CDVDInputStream* pInputStream, bool fileinfo) +{ + if (!pInputStream) + return NULL; + + // Try to open the AirTunes demuxer + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FILE) && pInputStream->GetContent().compare("audio/x-xbmc-pcm") == 0 ) + { + // audio/x-xbmc-pcm this is the used codec for AirTunes + // (apples audio only streaming) + unique_ptr demuxer(new CDVDDemuxBXA()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + + // Try to open CDDA demuxer + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FILE) && pInputStream->GetContent().compare("application/octet-stream") == 0) + { + std::string filename = pInputStream->GetFileName(); + if (filename.substr(0, 7) == "cdda://") + { + CLog::Log(LOGDEBUG, "DVDFactoryDemuxer: Stream is probably CD audio. Creating CDDA demuxer."); + + unique_ptr demuxer(new CDVDDemuxCDDA()); + if (demuxer->Open(pInputStream)) + { + return demuxer.release(); + } + } + } + + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_HTTP)) + { + CDVDInputStreamHttp* pHttpStream = (CDVDInputStreamHttp*)pInputStream; + CHttpHeader *header = pHttpStream->GetHttpHeader(); + + /* check so we got the meta information as requested in our http header */ + if( header->GetValue("icy-metaint").length() > 0 ) + { + unique_ptr demuxer(new CDVDDemuxShoutcast()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + } + +#ifdef HAS_FILESYSTEM_HTSP + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP)) + { + unique_ptr demuxer(new CDVDDemuxHTSP()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } +#endif + + bool streaminfo = true; /* Look for streams before playback */ + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_PVRMANAGER)) + { + CDVDInputStreamPVRManager* pInputStreamPVR = (CDVDInputStreamPVRManager*)pInputStream; + CDVDInputStream* pOtherStream = pInputStreamPVR->GetOtherStream(); + + /* Don't parse the streaminfo for some cases of streams to reduce the channel switch time */ + bool useFastswitch = URIUtils::IsUsingFastSwitch(pInputStream->GetFileName()); + streaminfo = !useFastswitch; + + if(pOtherStream) + { + /* Used for MediaPortal PVR addon (uses PVR otherstream for playback of rtsp streams) */ + if (pOtherStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + unique_ptr demuxer(new CDVDDemuxFFmpeg()); + if(demuxer->Open(pOtherStream, streaminfo)) + return demuxer.release(); + else + return NULL; + } + } + + /* Use PVR demuxer only for live streams */ + if (URIUtils::IsPVRChannel(pInputStream->GetFileName())) + { + std::shared_ptr client; + if (g_PVRClients->GetPlayingClient(client) && + client->HandlesDemuxing()) + { + unique_ptr demuxer(new CDVDDemuxPVRClient()); + if(demuxer->Open(pInputStream)) + return demuxer.release(); + else + return NULL; + } + } + } + + if (pInputStream->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) + { + bool useFastswitch = URIUtils::IsUsingFastSwitch(pInputStream->GetFileName()); + streaminfo = !useFastswitch; + } + + unique_ptr demuxer(new CDVDDemuxFFmpeg()); + if(demuxer->Open(pInputStream, streaminfo, fileinfo)) + return demuxer.release(); + else + return NULL; +} + diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h new file mode 100644 index 0000000..8281d28 --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/DVDFactoryDemuxer.h @@ -0,0 +1,30 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +class CDVDDemux; +class CDVDInputStream; + +class CDVDFactoryDemuxer +{ +public: + static CDVDDemux* CreateDemuxer(CDVDInputStream* pInputStream, bool fileinfo = false); +}; diff --git a/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in new file mode 100644 index 0000000..98493fe --- /dev/null +++ b/xbmc/cores/dvdplayer/DVDDemuxers/Makefile.in @@ -0,0 +1,19 @@ +INCLUDES+=-I@abs_top_srcdir@/xbmc/cores/dvdplayer + +SRCS = DVDDemux.cpp +SRCS += DVDDemuxBXA.cpp +SRCS += DVDDemuxCDDA.cpp +SRCS += DVDDemuxFFmpeg.cpp +SRCS += DVDDemuxHTSP.cpp +SRCS += DVDDemuxPVRClient.cpp +SRCS += DVDDemuxShoutcast.cpp +SRCS += DVDDemuxUtils.cpp +SRCS += DVDDemuxVobsub.cpp +SRCS += DVDDemuxCC.cpp +SRCS += DVDFactoryDemuxer.cpp + +LIB = DVDDemuxers.a + +include @abs_top_srcdir@/Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + -- cgit v1.2.3