From 6f9dbe14af7230c9458370c4cdc09b849680e182 Mon Sep 17 00:00:00 2001 From: verybadsoldier Date: Sat, 15 Jun 2013 19:54:33 +0200 Subject: [PATCH 01/15] added patch to handle SMB and MySQL after suspend more gracefully --- ...smbdeinit-and-wait-for-nic-on-wakeup.patch | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 packages/mediacenter/xbmc/patches/12.2.0/xbmc-990.24-smbdeinit-and-wait-for-nic-on-wakeup.patch diff --git a/packages/mediacenter/xbmc/patches/12.2.0/xbmc-990.24-smbdeinit-and-wait-for-nic-on-wakeup.patch b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-990.24-smbdeinit-and-wait-for-nic-on-wakeup.patch new file mode 100644 index 0000000000..51c30b698d --- /dev/null +++ b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-990.24-smbdeinit-and-wait-for-nic-on-wakeup.patch @@ -0,0 +1,190 @@ +diff -rupN xbmc-upstream/xbmc/Application.cpp xbmc-vbs/xbmc/Application.cpp +--- xbmc-upstream/xbmc/Application.cpp 2013-06-12 00:24:40.486262597 +0200 ++++ xbmc-vbs/xbmc/Application.cpp 2013-06-12 01:23:11.948400122 +0200 +@@ -5907,3 +5907,33 @@ CPerformanceStats &CApplication::GetPerf + } + #endif + ++void CApplication::CloseNetworkShares() ++{ ++ CLog::Log(LOGDEBUG,"CApplication::CloseNetworkShares: Closing all network shares"); ++ ++#if defined(HAS_FILESYSTEM_SMB) && !defined(_WIN32) ++ smb.Deinit(); ++#endif ++} ++ ++void CApplication::StopAddonServices() ++{ ++ CLog::Log(LOGDEBUG,"CApplication::StopAddonServices"); ++ CAddonMgr::Get().StopServices(false); ++} ++ ++void CApplication::StartAddonServices() ++{ ++ CLog::Log(LOGDEBUG,"CApplication::StartAddonServices - before login services"); ++ ++ //we start all services that run before login ++ CAddonMgr::Get().StartServices(true); ++ ++ //this is meant to find out if we are logged in already ++ //so only start after-login services if we are not seeing the login screen ++ if (g_windowManager.GetActiveWindow() != WINDOW_LOGIN_SCREEN) ++ { ++ CLog::Log(LOGDEBUG,"CApplication::StartAddonServices - No login screen? So starting after-login-services also"); ++ CAddonMgr::Get().StartServices(false); ++ } ++} +diff -rupN xbmc-upstream/xbmc/Application.h xbmc-vbs/xbmc/Application.h +--- xbmc-upstream/xbmc/Application.h 2013-06-12 00:24:40.494262695 +0200 ++++ xbmc-vbs/xbmc/Application.h 2013-06-12 01:15:31.530852337 +0200 +@@ -204,6 +204,9 @@ public: + void CheckPlayingProgress(); + void CheckAudioScrobblerStatus(); + void ActivateScreenSaver(bool forceType = false); ++ void CloseNetworkShares(); ++ void StartAddonServices(); ++ void StopAddonServices(); + + virtual void Process(); + void ProcessSlow(); +diff -rupN xbmc-upstream/xbmc/filesystem/SmbFile.cpp xbmc-vbs/xbmc/filesystem/SmbFile.cpp +--- xbmc-upstream/xbmc/filesystem/SmbFile.cpp 2013-06-12 00:24:40.962268274 +0200 ++++ xbmc-vbs/xbmc/filesystem/SmbFile.cpp 2013-06-12 01:23:11.948400122 +0200 +@@ -92,6 +92,7 @@ void CSMB::Deinit() + void CSMB::Init() + { + CSingleLock lock(*this); ++ + if (!m_context) + { + #ifdef TARGET_POSIX +diff -rupN xbmc-upstream/xbmc/powermanagement/PowerManager.cpp xbmc-vbs/xbmc/powermanagement/PowerManager.cpp +--- xbmc-upstream/xbmc/powermanagement/PowerManager.cpp 2013-06-12 00:24:41.634276292 +0200 ++++ xbmc-vbs/xbmc/powermanagement/PowerManager.cpp 2013-06-12 01:15:32.166859998 +0200 +@@ -23,6 +23,7 @@ + #include "Application.h" + #include "cores/AudioEngine/AEFactory.h" + #include "input/KeyboardStat.h" ++#include "network/Network.h" + #include "settings/GUISettings.h" + #include "windowing/WindowingFactory.h" + #include "utils/log.h" +@@ -32,6 +33,7 @@ + #include "guilib/LocalizeStrings.h" + #include "guilib/GraphicContext.h" + #include "dialogs/GUIDialogKaiToast.h" ++#include "settings/AdvancedSettings.h" + + #ifdef HAS_LCD + #include "utils/LCDFactory.h" +@@ -146,12 +148,22 @@ bool CPowerManager::Powerdown() + + bool CPowerManager::Suspend() + { +- return CanSuspend() ? m_instance->Suspend() : false; ++ if (!CanSuspend()) ++ return false; ++ ++ OnPrepareSleep(); ++ ++ return m_instance->Suspend(); + } + + bool CPowerManager::Hibernate() + { +- return CanHibernate() ? m_instance->Hibernate() : false; ++ if (!CanHibernate()) ++ return false; ++ ++ OnPrepareSleep(); ++ ++ return m_instance->Hibernate(); + } + bool CPowerManager::Reboot() + { +@@ -188,6 +200,16 @@ void CPowerManager::ProcessEvents() + m_instance->PumpPowerEvents(this); + } + ++void CPowerManager::OnPrepareSleep() ++{ ++ CLog::Log(LOGNOTICE, "%s: Preparing sleep", __FUNCTION__); ++ ++ //stop all addon services here ++ //we do this here instead in OnSleep cause according to DBUS specification we only have 1 second of time in OnSleep ++ //so shutdowns that may potentially take longer should be issued in here ++ g_application.StopAddonServices(); ++} ++ + void CPowerManager::OnSleep() + { + CAnnouncementManager::Announce(System, "xbmc", "OnSleep"); +@@ -207,13 +229,41 @@ void CPowerManager::OnSleep() + g_application.StopPlaying(); + g_application.StopShutdownTimer(); + g_application.StopScreenSaverTimer(); ++ g_application.CloseNetworkShares(); + CAEFactory::Suspend(); + } + ++void CPowerManager::WaitForNet() ++{ ++ CLog::Log(LOGDEBUG, "%s: Waithing for first NIC to come up", __FUNCTION__); ++ ++ const unsigned maxLoopCount = 50u; ++ const unsigned sleepTimeMs = 200u; ++ ++ for(unsigned i=0; i < 50; ++i) ++ { ++ CNetworkInterface* pIface = g_application.getNetwork().GetFirstConnectedInterface(); ++ if (pIface && pIface->IsEnabled() && pIface->IsConnected()) ++ { ++ CLog::Log(LOGDEBUG, "%s: NIC is up after waiting %d ms", __FUNCTION__, i * sleepTimeMs); ++ return; ++ } ++ ++ Sleep(sleepTimeMs); ++ } ++ ++ CLog::Log(LOGDEBUG, "%s: NIC did not come up within %d ms... Lets give up...", __FUNCTION__, maxLoopCount * sleepTimeMs); ++} ++ + void CPowerManager::OnWake() + { + CLog::Log(LOGNOTICE, "%s: Running resume jobs", __FUNCTION__); + ++ WaitForNet(); ++ ++ //re-start addon services ++ g_application.StartAddonServices(); ++ + // reset out timers + g_application.ResetShutdownTimers(); + +@@ -248,6 +298,7 @@ void CPowerManager::OnWake() + #endif + + CAEFactory::Resume(); ++ + g_application.UpdateLibraries(); + g_weatherManager.Refresh(); + +diff -rupN xbmc-upstream/xbmc/powermanagement/PowerManager.h xbmc-vbs/xbmc/powermanagement/PowerManager.h +--- xbmc-upstream/xbmc/powermanagement/PowerManager.h 2013-06-12 00:24:41.634276292 +0200 ++++ xbmc-vbs/xbmc/powermanagement/PowerManager.h 2013-06-12 01:15:32.166859998 +0200 +@@ -67,11 +67,14 @@ public: + + void ProcessEvents(); + private: ++ void OnPrepareSleep(); + void OnSleep(); + void OnWake(); + + void OnLowBattery(); + ++ void WaitForNet(); ++ + IPowerSyscall *m_instance; + }; + From 0bc543717bf0a3193ce109b75920d2bd9d36f851 Mon Sep 17 00:00:00 2001 From: Henk Wiedig Date: Tue, 18 Jun 2013 19:28:57 +0200 Subject: [PATCH 02/15] xorg: silent xorg log --- packages/x11/xserver/xorg-server/scripts/xorg_start | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/x11/xserver/xorg-server/scripts/xorg_start b/packages/x11/xserver/xorg-server/scripts/xorg_start index c89762f723..29ab52e9fa 100755 --- a/packages/x11/xserver/xorg-server/scripts/xorg_start +++ b/packages/x11/xserver/xorg-server/scripts/xorg_start @@ -47,6 +47,8 @@ if [ "$RUNLEVEL" = openelec ]; then if [ "$DEBUG" = yes ]; then XORG_ARGS="$XORG_ARGS -logverbose 6 -verbose 6" + else + XORG_ARGS="$XORG_ARGS -logverbose 0 -verbose 0" fi # load user defined xorg.conf, if exist From fbcc2e462dc3ec394f331c1fcb461a614ef2b5e6 Mon Sep 17 00:00:00 2001 From: fritsch Date: Tue, 18 Jun 2013 08:21:46 +0200 Subject: [PATCH 03/15] 3.9.6 rl8169 checksum fix --- .../patches/3.9.6/linux-701-fix-r8169.patch | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 packages/linux/patches/3.9.6/linux-701-fix-r8169.patch diff --git a/packages/linux/patches/3.9.6/linux-701-fix-r8169.patch b/packages/linux/patches/3.9.6/linux-701-fix-r8169.patch new file mode 100644 index 0000000000..5c1244c887 --- /dev/null +++ b/packages/linux/patches/3.9.6/linux-701-fix-r8169.patch @@ -0,0 +1,110 @@ +From b423e9ae49d78ea3f53b131c8d5a6087aed16fd6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?fran=C3=A7ois=20romieu?= +Date: Sat, 18 May 2013 01:24:46 +0000 +Subject: [PATCH] r8169: fix offloaded tx checksum for small packets. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +8168evl offloaded checksums are wrong since commit +e5195c1f31f399289347e043d6abf3ffa80f0005 ("r8169: fix 8168evl frame padding.") +pads small packets to 60 bytes (without ethernet checksum). Typical symptoms +appear as UDP checksums which are wrong by the count of added bytes. + +It isn't worth compensating. Let the driver checksum. + +Due to the skb length changes, TSO code is moved before the Tx descriptor gets +written. + +Signed-off-by: Francois Romieu +Tested-by: Holger Hoffstätte +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/realtek/r8169.c | 41 ++++++++++++++++++++++++------------ + 1 file changed, 27 insertions(+), 14 deletions(-) + +diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c +index 79c520b..393f961 100644 +--- a/drivers/net/ethernet/realtek/r8169.c ++++ b/drivers/net/ethernet/realtek/r8169.c +@@ -5856,7 +5856,20 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb, + return -EIO; + } + +-static inline void rtl8169_tso_csum(struct rtl8169_private *tp, ++static bool rtl_skb_pad(struct sk_buff *skb) ++{ ++ if (skb_padto(skb, ETH_ZLEN)) ++ return false; ++ skb_put(skb, ETH_ZLEN - skb->len); ++ return true; ++} ++ ++static bool rtl_test_hw_pad_bug(struct rtl8169_private *tp, struct sk_buff *skb) ++{ ++ return skb->len < ETH_ZLEN && tp->mac_version == RTL_GIGA_MAC_VER_34; ++} ++ ++static inline bool rtl8169_tso_csum(struct rtl8169_private *tp, + struct sk_buff *skb, u32 *opts) + { + const struct rtl_tx_desc_info *info = tx_desc_info + tp->txd_version; +@@ -5869,13 +5882,20 @@ static inline void rtl8169_tso_csum(struct rtl8169_private *tp, + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + const struct iphdr *ip = ip_hdr(skb); + ++ if (unlikely(rtl_test_hw_pad_bug(tp, skb))) ++ return skb_checksum_help(skb) == 0 && rtl_skb_pad(skb); ++ + if (ip->protocol == IPPROTO_TCP) + opts[offset] |= info->checksum.tcp; + else if (ip->protocol == IPPROTO_UDP) + opts[offset] |= info->checksum.udp; + else + WARN_ON_ONCE(1); ++ } else { ++ if (unlikely(rtl_test_hw_pad_bug(tp, skb))) ++ return rtl_skb_pad(skb); + } ++ return true; + } + + static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, +@@ -5896,17 +5916,15 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, + goto err_stop_0; + } + +- /* 8168evl does not automatically pad to minimum length. */ +- if (unlikely(tp->mac_version == RTL_GIGA_MAC_VER_34 && +- skb->len < ETH_ZLEN)) { +- if (skb_padto(skb, ETH_ZLEN)) +- goto err_update_stats; +- skb_put(skb, ETH_ZLEN - skb->len); +- } +- + if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) + goto err_stop_0; + ++ opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb)); ++ opts[0] = DescOwn; ++ ++ if (!rtl8169_tso_csum(tp, skb, opts)) ++ goto err_update_stats; ++ + len = skb_headlen(skb); + mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(d, mapping))) { +@@ -5918,11 +5936,6 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, + tp->tx_skb[entry].len = len; + txd->addr = cpu_to_le64(mapping); + +- opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb)); +- opts[0] = DescOwn; +- +- rtl8169_tso_csum(tp, skb, opts); +- + frags = rtl8169_xmit_frags(tp, skb, opts); + if (frags < 0) + goto err_dma_1; +-- +1.8.1.6 From 9381040a67ee2a2a4b3da8c2cfbb18530e70aeb4 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 06:03:32 +0200 Subject: [PATCH 04/15] xbmc (gotham): update to xbmc-6aa6247 Signed-off-by: Stephan Raue --- .../mediacenter/xbmc-theme-Confluence/meta | 2 +- packages/mediacenter/xbmc/meta | 2 +- ...1-texturepacker-hostflags-and-rework.patch | 0 ...HONOPTIMIZE_with_external_Python-0.1.patch | 0 ...bmc-453-add_openelec.tv_RSS_news-0.1.patch | 0 ...add_as.xml_busy_dialog_delay_control.patch | 0 ...s-mark_our_wrapped_functions_as_used.patch | 0 .../xbmc-995.01-xvba_support-a284bab.patch | 0 .../xbmc-995.10-disable-alt-tab.patch | 0 .../6aa6247/xbmc-995.20-segfault_fix.patch | 29 +++++++++++++++++++ .../xbmc-999.01-automake-1.13.patch | 0 11 files changed, 31 insertions(+), 2 deletions(-) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-321-texturepacker-hostflags-and-rework.patch (100%) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-408-enable_PYTHONOPTIMIZE_with_external_Python-0.1.patch (100%) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-453-add_openelec.tv_RSS_news-0.1.patch (100%) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-602-add_as.xml_busy_dialog_delay_control.patch (100%) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-990.15-depends-mark_our_wrapped_functions_as_used.patch (100%) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-995.01-xvba_support-a284bab.patch (100%) rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-995.10-disable-alt-tab.patch (100%) create mode 100644 packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch rename packages/mediacenter/xbmc/patches/{57f529b => 6aa6247}/xbmc-999.01-automake-1.13.patch (100%) diff --git a/packages/mediacenter/xbmc-theme-Confluence/meta b/packages/mediacenter/xbmc-theme-Confluence/meta index 37a944e953..77bdd4fba4 100644 --- a/packages/mediacenter/xbmc-theme-Confluence/meta +++ b/packages/mediacenter/xbmc-theme-Confluence/meta @@ -21,7 +21,7 @@ PKG_NAME="xbmc-theme-Confluence" PKG_VERSION="12.2.0" if [ "$XBMC" = "master" ]; then - PKG_VERSION="57f529b" + PKG_VERSION="6aa6247" elif [ "$XBMC" = "xbmc-aml" ]; then PKG_VERSION="aml-frodo-d9119f2" fi diff --git a/packages/mediacenter/xbmc/meta b/packages/mediacenter/xbmc/meta index 604b0c61d0..5c08ada228 100644 --- a/packages/mediacenter/xbmc/meta +++ b/packages/mediacenter/xbmc/meta @@ -21,7 +21,7 @@ PKG_NAME="xbmc" PKG_VERSION="12.2.0" if [ "$XBMC" = "master" ]; then - PKG_VERSION="57f529b" + PKG_VERSION="6aa6247" elif [ "$XBMC" = "xbmc-aml" ]; then PKG_VERSION="aml-frodo-d9119f2" fi diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-321-texturepacker-hostflags-and-rework.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-321-texturepacker-hostflags-and-rework.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-321-texturepacker-hostflags-and-rework.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-321-texturepacker-hostflags-and-rework.patch diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-408-enable_PYTHONOPTIMIZE_with_external_Python-0.1.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-408-enable_PYTHONOPTIMIZE_with_external_Python-0.1.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-408-enable_PYTHONOPTIMIZE_with_external_Python-0.1.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-408-enable_PYTHONOPTIMIZE_with_external_Python-0.1.patch diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-453-add_openelec.tv_RSS_news-0.1.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-453-add_openelec.tv_RSS_news-0.1.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-453-add_openelec.tv_RSS_news-0.1.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-453-add_openelec.tv_RSS_news-0.1.patch diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-602-add_as.xml_busy_dialog_delay_control.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-602-add_as.xml_busy_dialog_delay_control.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-602-add_as.xml_busy_dialog_delay_control.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-602-add_as.xml_busy_dialog_delay_control.patch diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-990.15-depends-mark_our_wrapped_functions_as_used.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-990.15-depends-mark_our_wrapped_functions_as_used.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-990.15-depends-mark_our_wrapped_functions_as_used.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-990.15-depends-mark_our_wrapped_functions_as_used.patch diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-995.01-xvba_support-a284bab.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-a284bab.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-995.01-xvba_support-a284bab.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-a284bab.patch diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-995.10-disable-alt-tab.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.10-disable-alt-tab.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-995.10-disable-alt-tab.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.10-disable-alt-tab.patch diff --git a/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch new file mode 100644 index 0000000000..60f3dd4b6a --- /dev/null +++ b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch @@ -0,0 +1,29 @@ +From 7dcf55c9560172db7e9daee2c20413cd8c9549dd Mon Sep 17 00:00:00 2001 +From: xbmc +Date: Sun, 16 Jun 2013 13:23:19 +0200 +Subject: [PATCH] renderer: delete fence on uninit + +--- + xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +index c663423..858d39d 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +@@ -1164,7 +1164,12 @@ void CLinuxRendererGL::UnInit() + + // YV12 textures + for (int i = 0; i < NUM_BUFFERS; ++i) ++ { + (this->*m_textureDelete)(i); ++ if (m_buffers[i].fence) ++ glDeleteSync(m_buffers[i].fence); ++ m_buffers[i].fence = None; ++ } + + // cleanup framebuffer object if it was in use + m_fbo.fbo.Cleanup(); +-- +1.8.1.6 + diff --git a/packages/mediacenter/xbmc/patches/57f529b/xbmc-999.01-automake-1.13.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-999.01-automake-1.13.patch similarity index 100% rename from packages/mediacenter/xbmc/patches/57f529b/xbmc-999.01-automake-1.13.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-999.01-automake-1.13.patch From 513f5dcdd46033d19e495554fcdd9ec1be215a71 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 06:04:40 +0200 Subject: [PATCH 05/15] ffmpeg: update upstream patches Signed-off-by: Stephan Raue --- ...6547d0be963e352de0cde1a6cba59ea2e78.patch} | 0 ...5995b88ec68a68cb8e496a008e1cd467077.patch} | 0 ...vaapi_render_picture-without-picture.patch | 22 +++++++++++++++++++ 3 files changed, 22 insertions(+) rename packages/multimedia/ffmpeg/patches/1.2/{ffmpeg-901.07.0036-backport-register-vdpau-hwaccel-for-mpeg12-fe1f36547d0be963e352de0cde1a6cba59ea2e78.patch => ffmpeg-0036-backport-register-vdpau-hwaccel-for-mpeg12-fe1f36547d0be963e352de0cde1a6cba59ea2e78.patch} (100%) rename packages/multimedia/ffmpeg/patches/1.2/{ffmpeg-901.07.0037-backport-fix-vdpau-vc1-interlace-modes-b37cc5995b88ec68a68cb8e496a008e1cd467077.patch => ffmpeg-0037-backport-fix-vdpau-vc1-interlace-modes-b37cc5995b88ec68a68cb8e496a008e1cd467077.patch} (100%) create mode 100644 packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0038-backport-vaapi-return-early-from-ff_vaapi_render_picture-without-picture.patch diff --git a/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-901.07.0036-backport-register-vdpau-hwaccel-for-mpeg12-fe1f36547d0be963e352de0cde1a6cba59ea2e78.patch b/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0036-backport-register-vdpau-hwaccel-for-mpeg12-fe1f36547d0be963e352de0cde1a6cba59ea2e78.patch similarity index 100% rename from packages/multimedia/ffmpeg/patches/1.2/ffmpeg-901.07.0036-backport-register-vdpau-hwaccel-for-mpeg12-fe1f36547d0be963e352de0cde1a6cba59ea2e78.patch rename to packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0036-backport-register-vdpau-hwaccel-for-mpeg12-fe1f36547d0be963e352de0cde1a6cba59ea2e78.patch diff --git a/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-901.07.0037-backport-fix-vdpau-vc1-interlace-modes-b37cc5995b88ec68a68cb8e496a008e1cd467077.patch b/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0037-backport-fix-vdpau-vc1-interlace-modes-b37cc5995b88ec68a68cb8e496a008e1cd467077.patch similarity index 100% rename from packages/multimedia/ffmpeg/patches/1.2/ffmpeg-901.07.0037-backport-fix-vdpau-vc1-interlace-modes-b37cc5995b88ec68a68cb8e496a008e1cd467077.patch rename to packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0037-backport-fix-vdpau-vc1-interlace-modes-b37cc5995b88ec68a68cb8e496a008e1cd467077.patch diff --git a/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0038-backport-vaapi-return-early-from-ff_vaapi_render_picture-without-picture.patch b/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0038-backport-vaapi-return-early-from-ff_vaapi_render_picture-without-picture.patch new file mode 100644 index 0000000000..f52c8313b4 --- /dev/null +++ b/packages/multimedia/ffmpeg/patches/1.2/ffmpeg-0038-backport-vaapi-return-early-from-ff_vaapi_render_picture-without-picture.patch @@ -0,0 +1,22 @@ +Subject: [libav-devel] [PATCH 1/2] vaapi: return early from ff_vaapi_render_picture() without picture +From: Janne Grunau janne-libav at jannau.net + +Fixes an assertion when called on uninitialized frame. Spotted after +seeking in vlc. (backported from libav mailing list) + +--- + +diff --git a/libavcodec/vaapi.c b/libavcodec/vaapi.c +index a220a9d..94959bf 100644 +--- a/libavcodec/vaapi.c ++++ b/libavcodec/vaapi.c +@@ -46,6 +46,9 @@ int ff_vaapi_render_picture(struct vaapi_context *vactx, VASurfaceID surface) + VABufferID va_buffers[3]; + unsigned int n_va_buffers = 0; + ++ if (!vactx->pic_param_buf_id) ++ return 0; ++ + vaUnmapBuffer(vactx->display, vactx->pic_param_buf_id); + va_buffers[n_va_buffers++] = vactx->pic_param_buf_id; + From 574d869ff828276b968653a566cf9050e934403c Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 06:36:08 +0200 Subject: [PATCH 06/15] xbmc (gotham): update xvba/vdpau patch Signed-off-by: Stephan Raue --- ...=> xbmc-995.01-xvba_support-c30c705.patch} | 537 +++++++++++------- .../6aa6247/xbmc-995.20-segfault_fix.patch | 29 - 2 files changed, 322 insertions(+), 244 deletions(-) rename packages/mediacenter/xbmc/patches/6aa6247/{xbmc-995.01-xvba_support-a284bab.patch => xbmc-995.01-xvba_support-c30c705.patch} (97%) delete mode 100644 packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch diff --git a/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-a284bab.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-c30c705.patch similarity index 97% rename from packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-a284bab.patch rename to packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-c30c705.patch index b06ae4c37a..8678c87c2c 100644 --- a/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-a284bab.patch +++ b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.01-xvba_support-c30c705.patch @@ -1,7 +1,7 @@ -From 88e00e0636826308db97e71b0fa58dc6b9fb0c72 Mon Sep 17 00:00:00 2001 +From e7ae857c25650217610157054c5b98dbec651181 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sat, 18 May 2013 20:17:57 +0200 -Subject: [PATCH 001/105] renderer: replace render buffer indices with queues +Subject: [PATCH 001/108] renderer: replace render buffer indices with queues --- xbmc/cores/VideoRenderers/RenderManager.cpp | 101 +++++++++++++++++----------- @@ -267,10 +267,10 @@ index d947e9e..03c5ab9 100644 1.8.1.6 -From d869662832c82228929dd9605f7d5ae33ab3bac0 Mon Sep 17 00:00:00 2001 +From af0696a005f386f577f62b065ff33280ac5e73d1 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 13 Jun 2013 23:29:45 +0200 -Subject: [PATCH 002/105] renderer: simplify code by always maintaining one +Subject: [PATCH 002/108] renderer: simplify code by always maintaining one front buffer --- @@ -401,10 +401,10 @@ index afc7223..5be41ab 100644 1.8.1.6 -From 39fd0caf3c1a93a28fe77d89cbd8a179daa9a77c Mon Sep 17 00:00:00 2001 +From ff674ec726f52e97f1b1baa75fd63ffc1108fa30 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 13 Jun 2013 23:39:55 +0200 -Subject: [PATCH 003/105] renderer: remove now pointless GetNext(Decode/Render) +Subject: [PATCH 003/108] renderer: remove now pointless GetNext(Decode/Render) function --- @@ -527,10 +527,10 @@ index 03c5ab9..f693d80 100644 1.8.1.6 -From b9fb6911879bc79213a8bd66db151bb3ad181586 Mon Sep 17 00:00:00 2001 +From a32759de68323854edb12bea577ebf58a69718c2 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Fri, 14 Jun 2013 00:25:37 +0200 -Subject: [PATCH 004/105] renderer: name present buffer structure to be able to +Subject: [PATCH 004/108] renderer: name present buffer structure to be able to reference it --- @@ -573,10 +573,10 @@ index f693d80..6dc2629 100644 1.8.1.6 -From 999f40f093961e6bcf1852166165e8a45096cbaa Mon Sep 17 00:00:00 2001 +From 8454f822388d1f2f44cd1a91df186d11fe75f74f Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Fri, 14 Jun 2013 00:29:39 +0200 -Subject: [PATCH 005/105] renderer: store bypass format in rendermanager +Subject: [PATCH 005/108] renderer: store bypass format in rendermanager instead The present method will be overwritten on each flippage @@ -632,10 +632,10 @@ index 6dc2629..1be4e2f 100644 1.8.1.6 -From e1ecc4b2f14b7707ce799623ef91204aa247a312 Mon Sep 17 00:00:00 2001 +From 258e28c9fb1b151822a398291d32dc3f1bc9cd75 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Fri, 14 Jun 2013 00:31:58 +0200 -Subject: [PATCH 006/105] renderer: drop copies of variables, use queue data +Subject: [PATCH 006/108] renderer: drop copies of variables, use queue data directly --- @@ -782,10 +782,10 @@ index 1be4e2f..83dc50a 100644 1.8.1.6 -From b263eefd78219d06d096a23ddee9f93c20253aa4 Mon Sep 17 00:00:00 2001 +From be1c14e77a0cd9c69b664a45afd9ac97ba7a46ea Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Fri, 14 Jun 2013 16:56:41 +0200 -Subject: [PATCH 007/105] renderer: grab presentlock in AddVideoPicture +Subject: [PATCH 007/108] renderer: grab presentlock in AddVideoPicture --- xbmc/cores/VideoRenderers/RenderManager.cpp | 10 +++++++--- @@ -816,10 +816,10 @@ index 255f108..bff4b3c 100644 1.8.1.6 -From c3124dc46ff9b71f2cb6fe2dc592006888008023 Mon Sep 17 00:00:00 2001 +From f34b42237b7770b0dd174b583c6aa39797364021 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Thu, 30 May 2013 10:56:06 +0200 -Subject: [PATCH 008/105] renderer: use fence for determination when a buffer +Subject: [PATCH 008/108] renderer: use fence for determination when a buffer is ready for reuse --- @@ -951,10 +951,10 @@ index bff4b3c..5cd3f06 100644 1.8.1.6 -From 31c9d071919582d7896c66c54f41b97554fd16f3 Mon Sep 17 00:00:00 2001 +From f981905cbb74326bc1b58bec6b5fd2ee5ce3dbd8 Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 28 May 2012 10:34:39 +0200 -Subject: [PATCH 009/105] videoplayer: adapt lateness detection and dropping to +Subject: [PATCH 009/108] videoplayer: adapt lateness detection and dropping to buffering --- @@ -1528,10 +1528,10 @@ index bf484ea..3669cc1 100644 1.8.1.6 -From b91985629642c325f9ff5103b1daa977176c65ae Mon Sep 17 00:00:00 2001 +From 8fa151760a5382a1bdf438b0df6a62445e01b1b8 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 2 Sep 2012 16:05:21 +0200 -Subject: [PATCH 010/105] video player: present correct pts to user for a/v +Subject: [PATCH 010/108] video player: present correct pts to user for a/v sync (after buffering in renderer) --- @@ -1599,10 +1599,10 @@ index 3669cc1..9477a6b 100644 1.8.1.6 -From facade51ccafe7aadb7968df83c387ff7df589af Mon Sep 17 00:00:00 2001 +From 3c87489ae25fea52d93b1e29bd061137fbcbe5e3 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 16 Feb 2013 18:25:53 +0100 -Subject: [PATCH 011/105] videoplayer: some rework and documentation +Subject: [PATCH 011/108] videoplayer: some rework and documentation --- .../dvdplayer/DVDCodecs/Video/DVDVideoCodec.h | 29 ++++++++++++++++++++-- @@ -1713,10 +1713,10 @@ index ba99804..56b32b9 100644 1.8.1.6 -From 1dabfa0fb962e464370dbc59898002261cf7f50a Mon Sep 17 00:00:00 2001 +From dc043a7bd54bdf787cca999103fa8ceb6252e3c0 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 7 Apr 2012 09:19:00 +0200 -Subject: [PATCH 012/105] vdpau: redesign +Subject: [PATCH 012/108] vdpau: redesign --- language/English/strings.po | 12 +- @@ -7868,10 +7868,10 @@ index e425327..3dae22c 100644 1.8.1.6 -From 705746efcbbd587ae1f47fe5ffc941266202b7f0 Mon Sep 17 00:00:00 2001 +From 287801ccf67d851f1f30e3033596b099e6d507b7 Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 12 Dec 2012 09:52:17 +0100 -Subject: [PATCH 013/105] vdpau: make interop gl default and remove setting, +Subject: [PATCH 013/108] vdpau: make interop gl default and remove setting, rename and intvert interop yuv --- @@ -7976,10 +7976,10 @@ index 000d2cb..63a462d 100644 1.8.1.6 -From a6e4f2d43298810d5919332bbe1000302f36d0a3 Mon Sep 17 00:00:00 2001 +From 2dea64df4a436a2a41e80d97fa4d58806fae7bfd Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 12 Dec 2012 18:34:47 +0100 -Subject: [PATCH 014/105] vdpau: drop studio level conversion +Subject: [PATCH 014/108] vdpau: drop studio level conversion --- xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 4 +- @@ -8131,10 +8131,10 @@ index 63a462d..2a6d64d 100644 1.8.1.6 -From 038d9cdeb2b568ffbe070fee621d36a0e6379893 Mon Sep 17 00:00:00 2001 +From 9b0ab0f30a9f31d9e758c86535a834b5967c97e5 Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 12 Dec 2012 20:28:49 +0100 -Subject: [PATCH 015/105] vdpau: observe ffmpeg tags for color space +Subject: [PATCH 015/108] vdpau: observe ffmpeg tags for color space --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 38 ++++++++++++++++++-------- @@ -8238,10 +8238,10 @@ index 5bee48b..ef99383 100644 1.8.1.6 -From 470140a62a85cbb0b06ddde88223a612a1195eb8 Mon Sep 17 00:00:00 2001 +From c4582331f3afcf3b7880a59ec60e2e52a9878eef Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 27 Jan 2013 12:10:19 +0100 -Subject: [PATCH 016/105] vdpau: switch off de-interlacing on ff +Subject: [PATCH 016/108] vdpau: switch off de-interlacing on ff --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 5 +++-- @@ -8267,10 +8267,10 @@ index b565e4a..a21d436 100644 1.8.1.6 -From 1a39dd0932221c8bba1d135c43fdfd5c3a8c6375 Mon Sep 17 00:00:00 2001 +From fb76ae7ed0bc6ffd55cc701efc850efb075aaa3e Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 2 Feb 2013 13:17:09 +0100 -Subject: [PATCH 017/105] vdpau: fix mp4 part2 decoding, activate by default +Subject: [PATCH 017/108] vdpau: fix mp4 part2 decoding, activate by default --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 3 +-- @@ -8310,10 +8310,10 @@ index d3206e5..55c1b49 100644 1.8.1.6 -From a386dd00b3dd3b8aa91fdc8f289017a059295a00 Mon Sep 17 00:00:00 2001 +From 517b2b55f69be205c2b73f460d6a001b7e34e833 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 2 Mar 2013 15:19:19 +0100 -Subject: [PATCH 018/105] vdpau: re-add limited range conversion +Subject: [PATCH 018/108] vdpau: re-add limited range conversion --- xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 4 +- @@ -8457,10 +8457,10 @@ index fe00c40..1c32ecb 100644 1.8.1.6 -From 1e90ddaf59b68c0074169e93e2d96982ccd798be Mon Sep 17 00:00:00 2001 +From dd41a59d732d8aa884c957f6e69f4ebad542d6fe Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 25 Sep 2012 12:14:15 +0200 -Subject: [PATCH 019/105] linuxrenderer: drop method RenderMultiPass +Subject: [PATCH 019/108] linuxrenderer: drop method RenderMultiPass --- xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 9 ++------- @@ -8510,10 +8510,10 @@ index d72eb64..6b9046d 100644 1.8.1.6 -From 8d13740c9ab4650f41b88b8fa81c02967757bdbb Mon Sep 17 00:00:00 2001 +From 94cad26332b6fa92b69de5c7b83e59eb8d141398 Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 25 Sep 2012 13:20:47 +0200 -Subject: [PATCH 020/105] linuxrenderer: implement progressive weave for vdpau +Subject: [PATCH 020/108] linuxrenderer: implement progressive weave for vdpau --- xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 55 +++++++++++++++++++-------- @@ -8641,10 +8641,10 @@ index 6b9046d..4494eca 100644 1.8.1.6 -From 04aab92691bdfa7a8176e0e2ef51d5ca8cad40db Mon Sep 17 00:00:00 2001 +From 0a06017adf0bdd04c970f1cf9617642033b36269 Mon Sep 17 00:00:00 2001 From: fritsch Date: Thu, 28 Mar 2013 10:38:37 +0100 -Subject: [PATCH 021/105] VDPAU: silence compiler warnings +Subject: [PATCH 021/108] VDPAU: silence compiler warnings --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 19 ++++++++----------- @@ -8740,10 +8740,10 @@ index 1c32ecb..d651be9 100644 1.8.1.6 -From bdcb116aaf97de94383de3ef56e29d24eb8b5ea2 Mon Sep 17 00:00:00 2001 +From 7fdbd9299f1fc81d5ae3942fcc779006e5abbd68 Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 25 Feb 2013 08:47:10 +0100 -Subject: [PATCH 022/105] vdpau: release more resources on pre-cleanup +Subject: [PATCH 022/108] vdpau: release more resources on pre-cleanup --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 72 +++++++++++++++++++++++--- @@ -8881,10 +8881,10 @@ index ef99383..39047b5 100644 1.8.1.6 -From bbbb3011193183da3023f16b73ecaab29919ec4f Mon Sep 17 00:00:00 2001 +From 982f9760fe5f54bfecb67613732f3a24b312ab74 Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 6 Mar 2013 07:35:10 +0100 -Subject: [PATCH 023/105] vdpau: set deinterlacing method to auto, if default +Subject: [PATCH 023/108] vdpau: set deinterlacing method to auto, if default method not supported --- @@ -8916,10 +8916,10 @@ index 624d4aa..e20a36d 100644 1.8.1.6 -From e5ecba1f99024cbec145c0d16ed88f197ca1da1a Mon Sep 17 00:00:00 2001 +From 0d5f6528fd65351078d0106c15f4cea4b7df9e20 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 21 Apr 2013 09:19:34 +0200 -Subject: [PATCH 024/105] vdpau: fix deadlock if decoder is closed while +Subject: [PATCH 024/108] vdpau: fix deadlock if decoder is closed while refresh rate changes --- @@ -8957,10 +8957,10 @@ index e20a36d..bb3cfb8 100644 1.8.1.6 -From 766dfea6c5bbd292c25bc399660ad39d44adb2dd Mon Sep 17 00:00:00 2001 +From 992adc2a595c04add7f07d1ff874513be12463a3 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sat, 1 Jun 2013 11:21:19 +0200 -Subject: [PATCH 025/105] renderer: bump buffers to 5 +Subject: [PATCH 025/108] renderer: bump buffers to 5 --- xbmc/cores/VideoRenderers/BaseRenderer.h | 2 +- @@ -8983,10 +8983,10 @@ index fe1f577..527ecf1 100644 1.8.1.6 -From 597129f194ea92e8fa2809098ed2ea4727cd765e Mon Sep 17 00:00:00 2001 +From 464bdd159494350142e6ec6dbf10b2ab5f8c4e1b Mon Sep 17 00:00:00 2001 From: wsnipex Date: Sun, 4 Nov 2012 14:05:52 +0100 -Subject: [PATCH 026/105] configure: add --enable-pvraddons-with-dependencies +Subject: [PATCH 026/108] configure: add --enable-pvraddons-with-dependencies switch for intree building of PVR Addons --- @@ -9033,10 +9033,10 @@ index e6658b6..ccf6399 100644 1.8.1.6 -From 1992c8989244ccb269cd03ee30f0ff0697b51359 Mon Sep 17 00:00:00 2001 +From 4783367b2b0039ea04f5cbe049dcfe3111ca7b70 Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 28 May 2012 10:41:31 +0200 -Subject: [PATCH 027/105] videoplayer: update frametime, it might change due to +Subject: [PATCH 027/108] videoplayer: update frametime, it might change due to fps detection --- @@ -9060,10 +9060,10 @@ index 3c30d0b..347e888 100644 1.8.1.6 -From f423275747e314946c12938de3113aa9f6612f94 Mon Sep 17 00:00:00 2001 +From 2e3f102f6bafffcab8e10bfb80967622864280fc Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 28 May 2012 10:43:06 +0200 -Subject: [PATCH 028/105] videoplayer: give streams with invalid fps a chance +Subject: [PATCH 028/108] videoplayer: give streams with invalid fps a chance for fps detection --- @@ -9087,10 +9087,10 @@ index 347e888..1a17145 100644 1.8.1.6 -From 27549dcb7a903e9e9b36768db6250530f3a57dd3 Mon Sep 17 00:00:00 2001 +From 6f163a35345945c740ec8d3e93e7402c05478e3b Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 28 May 2012 10:49:05 +0200 -Subject: [PATCH 029/105] dvdplayer: allow rewinding at end of stream, do a +Subject: [PATCH 029/108] dvdplayer: allow rewinding at end of stream, do a seek after rewind --- @@ -9098,7 +9098,7 @@ Subject: [PATCH 029/105] dvdplayer: allow rewinding at end of stream, do a 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp -index 22bc1e8..fefb6f9 100644 +index c944164..d2c164d 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp @@ -1555,7 +1555,7 @@ void CDVDPlayer::HandlePlaySpeed() @@ -9127,10 +9127,10 @@ index 22bc1e8..fefb6f9 100644 1.8.1.6 -From 705294ff0e4ec9ca302255f3535ae30d38758ea3 Mon Sep 17 00:00:00 2001 +From 2c81cdc7e8984651edcda1a8385075ccf12c2a1c Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 15:22:05 +0200 -Subject: [PATCH 030/105] X11: ditch SDL for video and window events +Subject: [PATCH 030/108] X11: ditch SDL for video and window events --- xbmc/Application.cpp | 2 +- @@ -9146,7 +9146,7 @@ Subject: [PATCH 030/105] X11: ditch SDL for video and window events create mode 100644 xbmc/windowing/WinEventsX11.h diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp -index 39182fc..d052473 100644 +index d4932d3..7dec8e2 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -828,7 +828,7 @@ bool CApplication::CreateGUI() @@ -10614,10 +10614,10 @@ index 3dae22c..25faaef 100644 1.8.1.6 -From 6274671f6f3a8bcbf4a469720eb3e62a37c981a5 Mon Sep 17 00:00:00 2001 +From c50ee0cb261b755753cc287a31b3fed67fa3dac7 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 15:24:22 +0200 -Subject: [PATCH 031/105] X11: Add xbmc icon +Subject: [PATCH 031/108] X11: Add xbmc icon --- xbmc/windowing/X11/WinSystemX11.cpp | 126 +++++++++++++++++++++++++++++++++++- @@ -10806,10 +10806,10 @@ index 25faaef..c1e6cf1 100644 1.8.1.6 -From c648d79cc836808afa03e2f1117d4d87f96ae528 Mon Sep 17 00:00:00 2001 +From 9e9a8f20ad945284ac3a0455eb5cf2a0106caf05 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 20 May 2012 14:11:26 +0200 -Subject: [PATCH 032/105] X11: add SDL joystick until we have a better solution +Subject: [PATCH 032/108] X11: add SDL joystick until we have a better solution --- xbmc/windowing/WinEventsX11.cpp | 26 ++++++++++++++++++++++++++ @@ -10863,10 +10863,10 @@ index 5a8bbb8..5bc1de0 100644 1.8.1.6 -From a016adb93e353b58669d300224ab20d493625097 Mon Sep 17 00:00:00 2001 +From 2d85f6a2114ddb9d723a568bec916cf31c0d6b3d Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 5 Jul 2012 12:35:55 +0200 -Subject: [PATCH 033/105] X11: factor out code handling device reset +Subject: [PATCH 033/108] X11: factor out code handling device reset notification --- @@ -10930,10 +10930,10 @@ index c1e6cf1..041ea55 100644 1.8.1.6 -From f99125e0e4ac175c81c3fb73527b9f6dc179cce5 Mon Sep 17 00:00:00 2001 +From 79f7e14ac628831289b6391e32b2271ff553ff8e Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 15:02:00 +0200 -Subject: [PATCH 034/105] X11: move xrandr events to WinEventsX11 +Subject: [PATCH 034/108] X11: move xrandr events to WinEventsX11 --- xbmc/windowing/WinEventsX11.cpp | 42 +++++++++++++++++++++++++++++++++++++ @@ -11077,10 +11077,10 @@ index fd51dc0..d495443 100644 1.8.1.6 -From d458dda426306558f216e1bee0f5877d753750de Mon Sep 17 00:00:00 2001 +From 7eab3afa072ded005ce14c63015e5a04cd7f5ec5 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 12 Apr 2012 15:43:56 +0200 -Subject: [PATCH 035/105] xrandr: remove method RestoreState +Subject: [PATCH 035/108] xrandr: remove method RestoreState --- xbmc/windowing/X11/WinSystemX11.cpp | 13 +++++++++++-- @@ -11159,10 +11159,10 @@ index e3450fe..cf22fbf 100644 1.8.1.6 -From cbb8d9a0752516f541fbd93630a5f3ae6de03e27 Mon Sep 17 00:00:00 2001 +From 6b3e1abe885b9cd26478a346e230f1c60ae96b1c Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 20 May 2012 13:17:10 +0200 -Subject: [PATCH 036/105] xrandr: observe orientation +Subject: [PATCH 036/108] xrandr: observe orientation --- xbmc/windowing/X11/WinSystemX11.cpp | 89 ++++++++++++++++++++++++++++++------- @@ -11377,10 +11377,10 @@ index cf22fbf..71ffab4 100644 1.8.1.6 -From c22d37a26225d66c37e3ce6157caf29da2a59780 Mon Sep 17 00:00:00 2001 +From 8dff8b91dcb92e4c670da2be641ec695f975bd47 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 11:54:15 +0200 -Subject: [PATCH 037/105] xrandr: allow getting info for multiple screen's +Subject: [PATCH 037/108] xrandr: allow getting info for multiple screen's Refactored by: Joakim Plate --- @@ -11555,10 +11555,10 @@ index 71ffab4..26c2653 100644 1.8.1.6 -From 9b85a1e4902fa6b0eeff0f8067f9686c1f78fa50 Mon Sep 17 00:00:00 2001 +From 565dbaa5a216e99fe991c96154dc26f01a80ffd5 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 11:44:00 +0200 -Subject: [PATCH 038/105] X11: fix multi-head setups +Subject: [PATCH 038/108] X11: fix multi-head setups --- language/English/strings.po | 4 +- @@ -12327,10 +12327,10 @@ index 0d4436b..0336b3b 100644 1.8.1.6 -From 1a640666efd77d03fe2de7d885cb34a6b3525c8f Mon Sep 17 00:00:00 2001 +From 901e1013dc9e99f1c01be6ad98ff2aef0dac4468 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 11:36:32 +0200 -Subject: [PATCH 039/105] X11: remove all DefaultScreen and RootWindow macros +Subject: [PATCH 039/108] X11: remove all DefaultScreen and RootWindow macros --- xbmc/windowing/X11/WinSystemX11.cpp | 6 +++--- @@ -12398,10 +12398,10 @@ index 1bea366..cc39720 100644 1.8.1.6 -From 847808bbcca7c4db170ac22f86063f3951161fd9 Mon Sep 17 00:00:00 2001 +From 21759dcbc20b24b7210a70270a2084559bdb7627 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 11:45:22 +0200 -Subject: [PATCH 040/105] X11: remove all DefaultScreen and RootWindow macros +Subject: [PATCH 040/108] X11: remove all DefaultScreen and RootWindow macros (VideoRefClock) Note this is on a separate display connection. @@ -12473,10 +12473,10 @@ index ca239cf..687e3ff 100644 1.8.1.6 -From 325ad5dedac7cfa5e4d2ec1a9163a24834fc466e Mon Sep 17 00:00:00 2001 +From 17bc1a357b38eba067bf5052e0a076980a3af194 Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 20 Jun 2012 17:37:11 +0200 -Subject: [PATCH 041/105] X11: recreate gl context after output has changed +Subject: [PATCH 041/108] X11: recreate gl context after output has changed --- xbmc/windowing/X11/WinSystemX11.cpp | 24 ++++++++++++++---------- @@ -12627,10 +12627,10 @@ index cc39720..dda7b14 100644 1.8.1.6 -From 937bd9f84d402e517b8f683841571b352a4d1733 Mon Sep 17 00:00:00 2001 +From fff1a8590ae02a1a0472384a6ecf77c6d9fec1b1 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 12:06:25 +0200 -Subject: [PATCH 042/105] X11: hook video reference clock in windowing +Subject: [PATCH 042/108] X11: hook video reference clock in windowing --- xbmc/video/VideoReferenceClock.cpp | 71 +++++++++++++++++++++++++++----------- @@ -12835,10 +12835,10 @@ index 1791570..bcabc9f 100644 1.8.1.6 -From 4ea6518fb04ef3672366d7a48642ac0461869032 Mon Sep 17 00:00:00 2001 +From 1137fceec8d5bb6596a025103b672fb703587b7a Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 21 Jun 2012 17:26:51 +0200 -Subject: [PATCH 043/105] X11: fix video calibrations +Subject: [PATCH 043/108] X11: fix video calibrations --- xbmc/windowing/WinSystem.h | 1 + @@ -12930,10 +12930,10 @@ index 2227320..630c0e2 100644 1.8.1.6 -From a22d9433ead7964315a90496d959528d0c20d3f3 Mon Sep 17 00:00:00 2001 +From e2cabb596594a2fa9b4cb6bebd2ae589b7c9a169 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 12:00:26 +0200 -Subject: [PATCH 044/105] X11: deactivate screen saver on startup +Subject: [PATCH 044/108] X11: deactivate screen saver on startup --- xbmc/windowing/X11/WinSystemX11.cpp | 29 +++++++++++++++++++++++++++++ @@ -13003,10 +13003,10 @@ index 630c0e2..f78f613 100644 1.8.1.6 -From 8078c2ec977e187ebd203d3cf44758cfc5bce0d5 Mon Sep 17 00:00:00 2001 +From 6ef08980a7963a66d8be88d95bf8d9a5aedd5fb8 Mon Sep 17 00:00:00 2001 From: FernetMenta Date: Thu, 5 Jul 2012 12:10:09 +0200 -Subject: [PATCH 045/105] X11: change method of going full-screen +Subject: [PATCH 045/108] X11: change method of going full-screen --- xbmc/windowing/X11/WinSystemX11.cpp | 9 ++++++++- @@ -13050,10 +13050,10 @@ index c643177..e1e1096 100644 1.8.1.6 -From 7735b3d52583702a043a4d8d1033437a40aca6c8 Mon Sep 17 00:00:00 2001 +From 56556b596712acb7c4a09362adb0b8401b49048a Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 28 Jun 2012 19:12:39 +0200 -Subject: [PATCH 046/105] X11: reset key repeat and key modifier on focus lost +Subject: [PATCH 046/108] X11: reset key repeat and key modifier on focus lost and gain --- @@ -13085,10 +13085,10 @@ index c58067b..c9f8a20 100644 1.8.1.6 -From 443a3f6ae52ab5d5e497fce83624290024878282 Mon Sep 17 00:00:00 2001 +From 37100804752375a14ae6a14ba79ff103d874e23e Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 5 Jul 2012 14:18:46 +0200 -Subject: [PATCH 047/105] X11: replace custom utf8 to unicode with charset +Subject: [PATCH 047/108] X11: replace custom utf8 to unicode with charset convertor (squash to x11 events) --- @@ -13305,10 +13305,10 @@ index 6100933..72955ad 100644 1.8.1.6 -From 8361e40ae3d7e25769b43611561847a64b2e1f5c Mon Sep 17 00:00:00 2001 +From fe5c3055858b6dba9149623404c7c98c46fafcd9 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 5 Jul 2012 14:23:54 +0200 -Subject: [PATCH 048/105] X11: fixed invalid usage of sizeof() (squash into x11 +Subject: [PATCH 048/108] X11: fixed invalid usage of sizeof() (squash into x11 changes) --- @@ -13372,10 +13372,10 @@ index 72955ad..102a076 100644 1.8.1.6 -From e7be23e2aa41168332f2cdd52cc208f2dcff1855 Mon Sep 17 00:00:00 2001 +From 1376a1adb5b37ccb5c19d25297db6262d411dad6 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 9 Jun 2012 18:23:53 +0200 -Subject: [PATCH 049/105] add missing keys to xbmc keytable +Subject: [PATCH 049/108] add missing keys to xbmc keytable --- xbmc/input/XBMC_keytable.cpp | 2 ++ @@ -13398,10 +13398,10 @@ index f18e9b1..066cd77 100644 1.8.1.6 -From 33dfa2f353b540be795dd6522ff29331b6ba75b9 Mon Sep 17 00:00:00 2001 +From 8f3f92e8b62fad1658134e71f4f784bf291c6525 Mon Sep 17 00:00:00 2001 From: xbmc Date: Fri, 16 Mar 2012 15:57:51 +0100 -Subject: [PATCH 050/105] videorefclock: temp deactivate of nv settings +Subject: [PATCH 050/108] videorefclock: temp deactivate of nv settings --- xbmc/video/VideoReferenceClock.cpp | 2 +- @@ -13424,10 +13424,10 @@ index 4287e8f..21f59b8 100644 1.8.1.6 -From 8abf5ad26e2e89960693e88a98e91fb339a73131 Mon Sep 17 00:00:00 2001 +From e3bae97ddd7285bdd073ebf7088610d827b4a6cf Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 20 Aug 2012 09:09:09 +0200 -Subject: [PATCH 051/105] videorefclock: ask graphics context for refresh rate +Subject: [PATCH 051/108] videorefclock: ask graphics context for refresh rate --- xbmc/video/VideoReferenceClock.cpp | 3 ++- @@ -13458,10 +13458,10 @@ index 21f59b8..0156b2c 100644 1.8.1.6 -From 47bb9cb1a96653dc1cdf7b0b105d40f444014cc2 Mon Sep 17 00:00:00 2001 +From a2bd30bc2c5cede7aac4059db39eba3a2f363cdf Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 9 Jul 2012 14:00:18 +0200 -Subject: [PATCH 052/105] X11: fix icon texture after +Subject: [PATCH 052/108] X11: fix icon texture after cc5ed3c2474084ebc0373a3046410e6f766e03f4 --- @@ -13569,10 +13569,10 @@ index e1e1096..ace57ff 100644 1.8.1.6 -From f7f4b3652498da8f3617e7fd90fd18ffb14ed6af Mon Sep 17 00:00:00 2001 +From d9720f2ad750c17466fed718b1c246e4af6f6797 Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 10 Jul 2012 11:14:12 +0200 -Subject: [PATCH 053/105] X11: check for window manager +Subject: [PATCH 053/108] X11: check for window manager --- xbmc/windowing/X11/WinSystemX11.cpp | 74 ++++++++++++++++++++++++++++++++++++- @@ -13693,10 +13693,10 @@ index f78f613..f479c27 100644 1.8.1.6 -From 10a6acbb4465393c41150c5094bc86346bb81f86 Mon Sep 17 00:00:00 2001 +From 658e79229d4831c816f6512507a15f8a7cea7d6e Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 12 Jul 2012 11:11:47 +0200 -Subject: [PATCH 054/105] X11: dont set window on xrandr if no mode available +Subject: [PATCH 054/108] X11: dont set window on xrandr if no mode available --- xbmc/windowing/X11/WinSystemX11.cpp | 11 ++++++----- @@ -13733,10 +13733,10 @@ index 188864b..076ed82 100644 1.8.1.6 -From b2bc899b89ff60b71237909b32ffc6e5cb725f5a Mon Sep 17 00:00:00 2001 +From feb73eb6c73b7ce8eb0e543232a04b738c621f37 Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 26 Jul 2012 09:34:28 +0200 -Subject: [PATCH 055/105] X11: fix crash after a resolution change on startup +Subject: [PATCH 055/108] X11: fix crash after a resolution change on startup --- xbmc/windowing/X11/WinSystemX11.cpp | 3 ++- @@ -13760,10 +13760,10 @@ index 076ed82..ee339d9 100644 1.8.1.6 -From 0079064cba196ef272acde11886a0505fc60b58a Mon Sep 17 00:00:00 2001 +From 02e236f99eedcbc73c42b2bb094a530ed46b551f Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 15 Sep 2012 18:27:29 +0200 -Subject: [PATCH 056/105] X11: lock graphics context in NotifyXRREvent +Subject: [PATCH 056/108] X11: lock graphics context in NotifyXRREvent --- xbmc/windowing/X11/WinSystemX11.cpp | 2 ++ @@ -13786,10 +13786,10 @@ index ee339d9..d2dcccd 100644 1.8.1.6 -From 1536bbf3d27cea035431bcb97f6e3f79b6bece79 Mon Sep 17 00:00:00 2001 +From 9ca7e258c30c1b5b5192a9c56e67805653e35204 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sat, 8 Oct 2011 16:45:13 +0200 -Subject: [PATCH 057/105] ffmpeg: add xvba hwaccel +Subject: [PATCH 057/108] ffmpeg: add xvba hwaccel --- lib/ffmpeg/configure | 11 ++ @@ -14638,10 +14638,10 @@ index 1c00ac4..6437e29 100644 1.8.1.6 -From b41b65badc8908db3490b1850b1a5069210b34eb Mon Sep 17 00:00:00 2001 +From 2b4c4e39e72458e3170c70a8f8149714384a3a25 Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 12 Apr 2012 12:09:31 +0200 -Subject: [PATCH 058/105] xvba: add decoder +Subject: [PATCH 058/108] xvba: add decoder --- configure.in | 47 + @@ -18093,10 +18093,10 @@ index 40d8c61..9cb1c5c 100644 1.8.1.6 -From de0ddf6dbec51bbe93244f222f493a81b5e3e353 Mon Sep 17 00:00:00 2001 +From 19dd3a7cd6b6c47d19f6369991612b22e033cb3a Mon Sep 17 00:00:00 2001 From: fritsch Date: Sun, 4 Nov 2012 16:24:10 +0100 -Subject: [PATCH 059/105] xvba: add string for available decoders - we are +Subject: [PATCH 059/108] xvba: add string for available decoders - we are important so make sure we are there --- @@ -18123,10 +18123,10 @@ index 9ad29f9..95cdb31 100644 1.8.1.6 -From a0430996f24cd9d1b4e38e38d8e76d6b770db556 Mon Sep 17 00:00:00 2001 +From 19113c6773bc804f41db5865f9048806988c8083 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 16 Jun 2012 12:46:30 +0200 -Subject: [PATCH 060/105] xvba: do not use vaapi if xvba is present +Subject: [PATCH 060/108] xvba: do not use vaapi if xvba is present --- xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp | 9 +++++++++ @@ -18156,10 +18156,10 @@ index dd96ce8..3943d52 100644 1.8.1.6 -From e9d091c51648ba8a70a1c14e2c8f7e55db43f580 Mon Sep 17 00:00:00 2001 +From cc88864ed74d42552ad637d9a7709928716ca739 Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 23 Aug 2012 19:39:49 +0200 -Subject: [PATCH 061/105] ffmpeg: add av_find_default_stream_index to interface +Subject: [PATCH 061/108] ffmpeg: add av_find_default_stream_index to interface --- lib/DllAvFormat.h | 4 ++++ @@ -18205,10 +18205,10 @@ index c3029db..4bccc8d 100644 1.8.1.6 -From 6b70e01e45612ce359194b8d34b36b18f789f3b2 Mon Sep 17 00:00:00 2001 +From 1223a2838c174e96f6415b624b1bc3dd187ec992 Mon Sep 17 00:00:00 2001 From: fritsch Date: Sat, 13 Apr 2013 11:30:39 +0200 -Subject: [PATCH 062/105] XVBA: revisit draw functions +Subject: [PATCH 062/108] XVBA: revisit draw functions --- lib/ffmpeg/libavcodec/xvba_h264.c | 2 +- @@ -18245,10 +18245,10 @@ index bf3d9c2..ff35a28 100644 1.8.1.6 -From f8ef893aa3984e12e1ef2b67dd6b7490171b5c9d Mon Sep 17 00:00:00 2001 +From a003efc4db7ac847791e4363757df7a8fc756a1c Mon Sep 17 00:00:00 2001 From: fritsch Date: Sat, 13 Apr 2013 12:06:02 +0200 -Subject: [PATCH 063/105] (ffmpeg): Make XVBA codec available +Subject: [PATCH 063/108] (ffmpeg): Make XVBA codec available --- lib/ffmpeg/libavcodec/vc1dec.c | 3 +++ @@ -18272,10 +18272,10 @@ index 2130c74..4d611f9 100644 1.8.1.6 -From e3b231d11e8444d99a2ef5d31fa04e3446f04dc4 Mon Sep 17 00:00:00 2001 +From 8cee691d35019c077ba92a0166349faf020a9569 Mon Sep 17 00:00:00 2001 From: fritsch Date: Sat, 13 Apr 2013 16:38:50 +0200 -Subject: [PATCH 064/105] ffmpeg: XVBA-VC1 use v->second_field instead of +Subject: [PATCH 064/108] ffmpeg: XVBA-VC1 use v->second_field instead of !s->first_field to make VC1 interlaced working --- @@ -18299,10 +18299,10 @@ index ff35a28..04e7983 100644 1.8.1.6 -From 29f72f6d5cf41af8063713b0fa33fa659c6ed9fa Mon Sep 17 00:00:00 2001 +From 47a427729d9b087d13fb868c25126e9b678a1359 Mon Sep 17 00:00:00 2001 From: fritsch Date: Sat, 27 Apr 2013 17:36:15 +0200 -Subject: [PATCH 065/105] XVBA: Only set second_field when we are interlaced +Subject: [PATCH 065/108] XVBA: Only set second_field when we are interlaced and an interlaced field --- @@ -18326,10 +18326,10 @@ index 04e7983..eb90c12 100644 1.8.1.6 -From 4c070d9226cee75752cf93dc2889984073e31e28 Mon Sep 17 00:00:00 2001 +From d29e61bd85fc5de04551db8945b206a123345f31 Mon Sep 17 00:00:00 2001 From: fritsch Date: Sat, 27 Apr 2013 22:08:50 +0200 -Subject: [PATCH 066/105] xvba: translate picture_structure to a value xvba +Subject: [PATCH 066/108] xvba: translate picture_structure to a value xvba understands --- @@ -18412,10 +18412,10 @@ index eb90c12..c98dba5 100644 1.8.1.6 -From f1802c0faa0e57a76620a1ee35d2b3438c942013 Mon Sep 17 00:00:00 2001 +From 4e6d7a038570b0c9f5e6ecadbbfd2c0489d7c74a Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 30 Apr 2013 21:18:55 +0200 -Subject: [PATCH 067/105] ffmpeg xvba: fix vc1 field interlace +Subject: [PATCH 067/108] ffmpeg xvba: fix vc1 field interlace --- lib/ffmpeg/libavcodec/xvba.c | 4 ++-- @@ -18467,10 +18467,10 @@ index c98dba5..e7a85a7 100644 1.8.1.6 -From 00da49aacf45f78fc0223319cf9c0e09b9e18641 Mon Sep 17 00:00:00 2001 +From 1222b33bb0662eb0e8766b930d6ece71d97b06bb Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 30 Apr 2013 21:19:07 +0200 -Subject: [PATCH 068/105] xvba: fix vc1 field interlace +Subject: [PATCH 068/108] xvba: fix vc1 field interlace --- xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp | 3 +++ @@ -18494,10 +18494,10 @@ index 43a331f..568632f 100644 1.8.1.6 -From 097eaa543ef9731e10fd63cdf481eda225762640 Mon Sep 17 00:00:00 2001 +From eb79bf151d602b8c84ac1b0539fc7baf7ea6fb7a Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 30 Apr 2013 21:40:55 +0200 -Subject: [PATCH 069/105] xvba: vc1 - honor psf +Subject: [PATCH 069/108] xvba: vc1 - honor psf --- xbmc/cores/dvdplayer/DVDCodecs/Video/XVBA.cpp | 3 +++ @@ -18528,10 +18528,10 @@ index 568632f..1db9363 100644 1.8.1.6 -From 59d0e6d2cd9053a9a321cb3fc419c86637c68d1f Mon Sep 17 00:00:00 2001 +From 13199aba1642585a76c70753f0a5dde73fd602cd Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 4 May 2013 10:31:32 +0200 -Subject: [PATCH 070/105] xvba: squash me, settings +Subject: [PATCH 070/108] xvba: squash me, settings --- xbmc/settings/Settings.cpp | 3 +++ @@ -18555,10 +18555,10 @@ index fdbc34e..efdde63 100644 1.8.1.6 -From 0aac10c8dc26f3eff6cddf791cfa481d706d5a6e Mon Sep 17 00:00:00 2001 +From 128a48ae2c438f287f0890cf0d417d190beed436 Mon Sep 17 00:00:00 2001 From: fritsch Date: Fri, 24 May 2013 12:02:02 +0200 -Subject: [PATCH 071/105] XVBA: Limit video to 2048x1152 as this is the max all +Subject: [PATCH 071/108] XVBA: Limit video to 2048x1152 as this is the max all blocks can handle --- @@ -18588,10 +18588,10 @@ index 1db9363..54d7aea 100644 1.8.1.6 -From a0194a86e40a2964869bc8c4cbd11f99359185ab Mon Sep 17 00:00:00 2001 +From 66dd4ead4323eaab122c7b28d78a694712ebfb51 Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 20 Aug 2012 16:06:39 +0200 -Subject: [PATCH 072/105] dvdplayer: observe pts counter overflow +Subject: [PATCH 072/108] dvdplayer: observe pts counter overflow --- .../cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp | 198 ++++++++++++++++++++- @@ -18878,10 +18878,10 @@ index b78094e..17d2f7a 100644 1.8.1.6 -From ccb5803c137d470b60b1ac82754629e55e1978b6 Mon Sep 17 00:00:00 2001 +From bbb0ee3ef49936ff4c0e5bd8921813f85f835002 Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 2 Oct 2012 13:02:10 +0200 -Subject: [PATCH 073/105] dvdplayer: avoid short screen flicker caused by +Subject: [PATCH 073/108] dvdplayer: avoid short screen flicker caused by unnecessary reconfigure of renderer --- @@ -18914,10 +18914,10 @@ index 41e64a5..74b4391 100644 1.8.1.6 -From 50b58f7a676c94df3fecfeb4feca994e2d346798 Mon Sep 17 00:00:00 2001 +From a24004ba055ccdff661bea0d6e7c24d8468f57a6 Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 11 Oct 2012 12:05:50 +0200 -Subject: [PATCH 074/105] vdpau: advanced settings for auto deinterlacing +Subject: [PATCH 074/108] vdpau: advanced settings for auto deinterlacing --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 8 ++++---- @@ -18983,10 +18983,10 @@ index d41cbcf..10adbb9 100644 1.8.1.6 -From 4cbd3ba1f53fbfc791dd087de4e01c3f071d36f1 Mon Sep 17 00:00:00 2001 +From 600eb385f152725667354b11027f15ac91cad675 Mon Sep 17 00:00:00 2001 From: xbmc Date: Fri, 2 Nov 2012 13:20:03 +0100 -Subject: [PATCH 075/105] player: fix rewind +Subject: [PATCH 075/108] player: fix rewind --- xbmc/cores/dvdplayer/DVDMessage.h | 5 ++++- @@ -19034,7 +19034,7 @@ index 3f65ced..ad434d2 100644 class CDVDMsgPlayerSeekChapter : public CDVDMsg diff --git a/xbmc/cores/dvdplayer/DVDPlayer.cpp b/xbmc/cores/dvdplayer/DVDPlayer.cpp -index fefb6f9..85f1a58 100644 +index d2c164d..1846d40 100644 --- a/xbmc/cores/dvdplayer/DVDPlayer.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayer.cpp @@ -1556,11 +1556,13 @@ void CDVDPlayer::HandlePlaySpeed() @@ -19201,10 +19201,10 @@ index 9477a6b..41e72c8 100644 1.8.1.6 -From 234a032a70b18a2e11afac671d8f97cf9c4afadb Mon Sep 17 00:00:00 2001 +From 7400869def01734e52f829376f6057720819c0ae Mon Sep 17 00:00:00 2001 From: xbmc Date: Fri, 23 Nov 2012 17:41:12 +0100 -Subject: [PATCH 076/105] xrandr: fix query for multiple screens +Subject: [PATCH 076/108] xrandr: fix query for multiple screens --- xbmc/windowing/X11/XRandR.cpp | 10 ++++++---- @@ -19245,10 +19245,10 @@ index 97b1e32..a3d3543 100644 1.8.1.6 -From 3eaa7c7f1063f559e5ac2e5a4bc99382177bd18c Mon Sep 17 00:00:00 2001 +From 799f6fee0638091c18d992e0903d2dbb4c8256ba Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 2 Dec 2012 15:46:55 +0100 -Subject: [PATCH 077/105] X11: add debug log to print out refresh after xrr +Subject: [PATCH 077/108] X11: add debug log to print out refresh after xrr event --- @@ -19276,10 +19276,10 @@ index d2dcccd..7403785 100644 1.8.1.6 -From 46f3774c71c82f05347985da908f7cae23b183d7 Mon Sep 17 00:00:00 2001 +From 47f804211c71e7700b14b3ac6936abbc9b377c17 Mon Sep 17 00:00:00 2001 From: xbmc Date: Tue, 11 Dec 2012 11:08:13 +0100 -Subject: [PATCH 078/105] X11: dont call XCloseDisplay on shutdown, it crashes +Subject: [PATCH 078/108] X11: dont call XCloseDisplay on shutdown, it crashes when powered doen by cec on ATI --- @@ -19304,10 +19304,10 @@ index 7403785..0e39867 100644 1.8.1.6 -From bf8b48724fee058a177410fc6d39eff15d050c5c Mon Sep 17 00:00:00 2001 +From 5e1de3a0d53d3456f4fbad541c162fb8c68ac378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Coutant?= Date: Wed, 12 Dec 2012 19:49:47 +0100 -Subject: [PATCH 079/105] x11: support for multiple x screens +Subject: [PATCH 079/108] x11: support for multiple x screens --- xbmc/windowing/X11/XRandR.cpp | 2 +- @@ -19330,10 +19330,10 @@ index a3d3543..8846bc6 100644 1.8.1.6 -From a6fd4f03aa7b1a677dc614d29881042c3211cbb2 Mon Sep 17 00:00:00 2001 +From 391502eea2356a8f4ee2cc4242240102e4c1367f Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 24 Dec 2012 16:02:42 +0100 -Subject: [PATCH 080/105] pvr: increase changes counter of stream on stream +Subject: [PATCH 080/108] pvr: increase changes counter of stream on stream change, cosmetics after dd307930d39d92f145a01a16600cd00e01ec39be --- @@ -19367,10 +19367,10 @@ index 2c10a7b..6ea1f23 100644 1.8.1.6 -From 44b87dd56ac6550acce9b6000ff190c89f8c17f6 Mon Sep 17 00:00:00 2001 +From f81e13d79d5975935746c0b3c525c86c20bc1fbc Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 17 Jan 2013 16:03:22 +0100 -Subject: [PATCH 081/105] X11: add keymapping for XF86XK_Sleep +Subject: [PATCH 081/108] X11: add keymapping for XF86XK_Sleep --- xbmc/windowing/WinEventsX11.cpp | 1 + @@ -19392,10 +19392,10 @@ index 4a5aab4..da5d412 100644 1.8.1.6 -From bbb47f08c0e9773d2ca2ec2dd7b3760fbc384654 Mon Sep 17 00:00:00 2001 +From 5b3918272a92652b6c0ba46686e7195e2d31334a Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 21 Jan 2013 09:00:19 +0100 -Subject: [PATCH 082/105] X11: remove toggle full screen after resume +Subject: [PATCH 082/108] X11: remove toggle full screen after resume --- xbmc/powermanagement/PowerManager.cpp | 5 ----- @@ -19421,10 +19421,10 @@ index f2a063f..f1e3c4f 100644 1.8.1.6 -From b56fae395a63564a5a48ffae2d9d5535b36d226a Mon Sep 17 00:00:00 2001 +From cd0e6cad21502e0d64092fc2be76c44fa59cf68b Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 23 Jan 2013 17:03:02 +0100 -Subject: [PATCH 083/105] xrandr: set screen on mode change command +Subject: [PATCH 083/108] xrandr: set screen on mode change command --- xbmc/windowing/X11/XRandR.cpp | 2 +- @@ -19447,10 +19447,10 @@ index 8846bc6..e6d6240 100644 1.8.1.6 -From 4740374a74ef4bd9a79b8441db7364432ce37df2 Mon Sep 17 00:00:00 2001 +From 5e6561e728d658a845ef4ab7961ce074ee5f5b4a Mon Sep 17 00:00:00 2001 From: xbmc Date: Wed, 23 Jan 2013 17:03:39 +0100 -Subject: [PATCH 084/105] X11: recreate glx context when output changes +Subject: [PATCH 084/108] X11: recreate glx context when output changes --- xbmc/windowing/X11/WinSystemX11.cpp | 6 +++--- @@ -19501,10 +19501,10 @@ index f479c27..7345c06 100644 1.8.1.6 -From 50c2359851b1a7deb0c00e8094aa3bc79c53c57e Mon Sep 17 00:00:00 2001 +From 856ef0fe858f030613c4a37f5ea2e2b4574fe7c5 Mon Sep 17 00:00:00 2001 From: xbmc Date: Fri, 14 Dec 2012 14:19:15 +0100 -Subject: [PATCH 085/105] pvr: do not show selection dialog for a single menu +Subject: [PATCH 085/108] pvr: do not show selection dialog for a single menu hook --- @@ -19547,10 +19547,10 @@ index 2b43bcb..d07f23d 100644 1.8.1.6 -From 7f7891ed08b742ef94a5d9a70680bace632d2cb8 Mon Sep 17 00:00:00 2001 +From 47da3eb6168e3b7fa1202f61b01c89c8a02709af Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 3 Feb 2013 08:17:16 +0100 -Subject: [PATCH 086/105] X11: use default screen parameters if no output +Subject: [PATCH 086/108] X11: use default screen parameters if no output connected --- @@ -19654,10 +19654,10 @@ index d865cd7..97975dd 100644 1.8.1.6 -From b491257c7c6c3cadd52552af52c81280b2d09da6 Mon Sep 17 00:00:00 2001 +From 4b962c36c4c65f26ef30a0f1be911d17be0e2aa5 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 23 Mar 2013 15:13:32 +0100 -Subject: [PATCH 087/105] X11: create parent window +Subject: [PATCH 087/108] X11: create parent window --- xbmc/windowing/X11/WinSystemX11.cpp | 69 +++++++++++++++++++++++-------------- @@ -19923,10 +19923,10 @@ index 7345c06..770ae84 100644 1.8.1.6 -From f83e6f35df169ecef7dc37e52395cb7a3f0eba44 Mon Sep 17 00:00:00 2001 +From fa91d1013c5431f19e166eba1553174698215133 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 24 Mar 2013 12:30:12 +0100 -Subject: [PATCH 088/105] X11: use system key repeat rate instead of hardcoded +Subject: [PATCH 088/108] X11: use system key repeat rate instead of hardcoded one, taken from 58fd64b194e38b73b5f3132744bab35e994e7441 --- @@ -20128,10 +20128,10 @@ index 102a076..5b1f3fa 100644 1.8.1.6 -From 42f5e2a5f225b13ec703d005c40768fe6c809734 Mon Sep 17 00:00:00 2001 +From 78fe3057ca88dfe4b77659dc90c817c4f4417b60 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sun, 24 Mar 2013 16:04:48 +0100 -Subject: [PATCH 089/105] linux: use CLOCK_MONOTONIC_RAW as this is not subject +Subject: [PATCH 089/108] linux: use CLOCK_MONOTONIC_RAW as this is not subject to NTP --- @@ -20169,17 +20169,17 @@ index 8304ef6..ba27257 100644 1.8.1.6 -From 17e7da272722e9b2e15ac32981ad0588f3bb50e4 Mon Sep 17 00:00:00 2001 +From 91015d3bcbf56dc5b1f930787e5544cfa3b2164c Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 28 Mar 2013 15:18:53 +0100 -Subject: [PATCH 090/105] OMXPlayer: some caching fixes for pvr +Subject: [PATCH 090/108] OMXPlayer: some caching fixes for pvr --- xbmc/cores/omxplayer/OMXPlayer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xbmc/cores/omxplayer/OMXPlayer.cpp b/xbmc/cores/omxplayer/OMXPlayer.cpp -index 31739ad..0ac3d5f 100644 +index 882c660..d9998c6 100644 --- a/xbmc/cores/omxplayer/OMXPlayer.cpp +++ b/xbmc/cores/omxplayer/OMXPlayer.cpp @@ -2353,7 +2353,8 @@ void COMXPlayer::HandleMessages() @@ -20196,17 +20196,17 @@ index 31739ad..0ac3d5f 100644 1.8.1.6 -From eb8a9f51678369d9fa89e0f863a3c8bbd3d0900f Mon Sep 17 00:00:00 2001 +From 463805b26a8a59cbdc0a347e71265aa869ac1203 Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 28 Mar 2013 20:50:59 +0100 -Subject: [PATCH 091/105] fix incorrect display of fps when dr kicks in +Subject: [PATCH 091/108] fix incorrect display of fps when dr kicks in --- xbmc/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp -index d052473..4a174a5 100644 +index 7dec8e2..ed4683e 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -2139,10 +2139,11 @@ void CApplication::Render() @@ -20226,10 +20226,10 @@ index d052473..4a174a5 100644 1.8.1.6 -From 249ddebfc12651d4a39c53b956183e941f35f1ed Mon Sep 17 00:00:00 2001 +From 7d5d30570dbd925f7838612eeaa5eac9610c3ec5 Mon Sep 17 00:00:00 2001 From: xbmc Date: Mon, 8 Apr 2013 11:18:31 +0200 -Subject: [PATCH 092/105] squash to dropping control +Subject: [PATCH 092/108] squash to dropping control --- xbmc/cores/dvdplayer/DVDPlayerVideo.cpp | 1 + @@ -20251,10 +20251,10 @@ index 11e0b26..3f915ce 100644 1.8.1.6 -From a3ddcdb193a73cef9979c8a4bb9416904aa8c058 Mon Sep 17 00:00:00 2001 +From 463e9e53e836e1cd63bc8ab5d2fe7dbb7cbda597 Mon Sep 17 00:00:00 2001 From: xbmc Date: Thu, 11 Apr 2013 12:33:46 +0200 -Subject: [PATCH 093/105] pvr: try SwtichChannel when selecting a channel via +Subject: [PATCH 093/108] pvr: try SwtichChannel when selecting a channel via EPG --- @@ -20289,10 +20289,10 @@ index 6b999e4..256a295 100644 1.8.1.6 -From c96b79e91757be2fa5814e2f3aff00c3741c7331 Mon Sep 17 00:00:00 2001 +From ae7f612abb8e6c9d63d729fee30490b962fd7619 Mon Sep 17 00:00:00 2001 From: xbmc Date: Sat, 13 Apr 2013 08:32:06 +0200 -Subject: [PATCH 094/105] X11: fix mouse coverage +Subject: [PATCH 094/108] X11: fix mouse coverage --- xbmc/windowing/X11/WinSystemX11.cpp | 11 ++++++++--- @@ -20358,10 +20358,10 @@ index 770ae84..084f546 100644 1.8.1.6 -From 7c7915b660e810f5d3adfbb47ea69e9cffa9d922 Mon Sep 17 00:00:00 2001 +From 97483917d15d4e5cd151e75964b294231c28e7c6 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Wed, 8 May 2013 13:14:58 +0200 -Subject: [PATCH 095/105] X11: fix incorrectly used screen num in desktop +Subject: [PATCH 095/108] X11: fix incorrectly used screen num in desktop resolution --- @@ -20396,10 +20396,10 @@ index bf95bc7..0221036 100644 1.8.1.6 -From d19e317f7a05fd53e211cbbefc86b0eb6c25c673 Mon Sep 17 00:00:00 2001 +From d0ae3aa55578b48d86b5baea3105a8650eb30995 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Thu, 9 May 2013 12:07:09 +0200 -Subject: [PATCH 096/105] X11: do not overwrite user selected monitor with +Subject: [PATCH 096/108] X11: do not overwrite user selected monitor with fallback --- @@ -20524,10 +20524,10 @@ index 084f546..30f5fa0 100644 1.8.1.6 -From d40bf02db479fe9afa00709b95666824f41dc665 Mon Sep 17 00:00:00 2001 +From 42c1bb44fea136828be5c99a122695cd0d108bce Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sun, 12 May 2013 10:50:30 +0200 -Subject: [PATCH 097/105] xrandr: add turn on/off to wrapper +Subject: [PATCH 097/108] xrandr: add turn on/off to wrapper --- xbmc/windowing/X11/XRandR.cpp | 78 +++++++++++++++++++++++++++++++++++++++---- @@ -20693,10 +20693,10 @@ index 26c2653..2741879 100644 1.8.1.6 -From d92dfda6e2f00703e5bd8685e98fd29c586497dd Mon Sep 17 00:00:00 2001 +From 47edb63e9aad15a900a6b7335c6a58aa9c905b9b Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sun, 19 May 2013 12:55:35 +0200 -Subject: [PATCH 098/105] xrandr: add GetPreferredMode to wrapper +Subject: [PATCH 098/108] xrandr: add GetPreferredMode to wrapper --- xbmc/windowing/X11/XRandR.cpp | 23 +++++++++++++++++++++++ @@ -20753,10 +20753,10 @@ index 2741879..24ad1d0 100644 1.8.1.6 -From f7b9460909f4ffbc6ac980188b33325f4696f2ff Mon Sep 17 00:00:00 2001 +From 8d67da3e905815a4540487de31eeaa5dd05300a7 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sat, 11 May 2013 17:12:12 +0200 -Subject: [PATCH 099/105] X11: multi-head improvement - poll for desired output +Subject: [PATCH 099/108] X11: multi-head improvement - poll for desired output if we do not get an xrr event --- @@ -21037,10 +21037,10 @@ index 380a194..650a6ef 100644 1.8.1.6 -From 76d65994815eeb4ee713cea1f81fff9240963bd5 Mon Sep 17 00:00:00 2001 +From 9b0e0365197809889bce839067e8282823d4ac4f Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Wed, 15 May 2013 09:14:34 +0200 -Subject: [PATCH 100/105] X11: ignore mouse move event form other windows +Subject: [PATCH 100/108] X11: ignore mouse move event form other windows --- xbmc/windowing/WinEventsX11.cpp | 4 +++- @@ -21072,10 +21072,10 @@ index 879d8f2..2ec9b6f 100644 1.8.1.6 -From 0a185e523678e7c4c621fd777a2bb588410b67d5 Mon Sep 17 00:00:00 2001 +From 09fa23a499bcabeb4d36cf198d30b2af536a0991 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Sun, 2 Jun 2013 14:53:45 +0200 -Subject: [PATCH 101/105] vdpau: fix segfault caused by uninitialized member +Subject: [PATCH 101/108] vdpau: fix segfault caused by uninitialized member --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 1 + @@ -21097,10 +21097,10 @@ index 1aeef6a..4543d3e 100644 1.8.1.6 -From ca43f3469f690b9e8a4dd818e2349618c760fb51 Mon Sep 17 00:00:00 2001 +From b11e45603a6319ac2878ff2416340a5fd0d23746 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Tue, 11 Jun 2013 16:13:45 +0200 -Subject: [PATCH 102/105] vdpau: sync video mixer +Subject: [PATCH 102/108] vdpau: sync video mixer --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 15 +++++++++++++++ @@ -21136,10 +21136,10 @@ index 4543d3e..93c1782 100644 1.8.1.6 -From f7b1f3a1dbf3e1a5440980e105b2b8ccd892b62d Mon Sep 17 00:00:00 2001 +From aae365a830c5a00ab398d9805a300b386d1fdc2a Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Tue, 11 Jun 2013 16:20:29 +0200 -Subject: [PATCH 103/105] renderer: allow some lateness within vblank interval +Subject: [PATCH 103/108] renderer: allow some lateness within vblank interval --- xbmc/cores/VideoRenderers/RenderManager.cpp | 12 ++++++++++-- @@ -21199,10 +21199,10 @@ index 2c5ccf4..90f8af9 100644 1.8.1.6 -From d465f05c1849e39448dec13e1106679e01944301 Mon Sep 17 00:00:00 2001 +From 1c240ea16b0ad99c01e5267eb22465e5df14bfa3 Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Fri, 14 Jun 2013 09:23:22 +0200 -Subject: [PATCH 104/105] vdpau: calculate timestamp of second field when doing +Subject: [PATCH 104/108] vdpau: calculate timestamp of second field when doing deinterlacing --- @@ -21228,10 +21228,10 @@ index 93c1782..ace5d49 100644 1.8.1.6 -From a284bab6839df46627cf20546013c24590dc9c03 Mon Sep 17 00:00:00 2001 +From ded355fc11f401021177b074b2ae6234212595cb Mon Sep 17 00:00:00 2001 From: Rainer Hochecker Date: Fri, 14 Jun 2013 10:46:58 +0200 -Subject: [PATCH 105/105] vdpau: comment debug log: long decoding time +Subject: [PATCH 105/108] vdpau: comment debug log: long decoding time --- xbmc/cores/dvdplayer/DVDCodecs/Video/VDPAU.cpp | 8 ++++---- @@ -21266,3 +21266,110 @@ index ace5d49..0c4b4d0 100644 -- 1.8.1.6 + +From 0392770b4a091859fd7150a86e27ce10c4c10abb Mon Sep 17 00:00:00 2001 +From: xbmc +Date: Sun, 16 Jun 2013 13:22:58 +0200 +Subject: [PATCH 106/108] X11: another fix for mouse coverage + +--- + xbmc/windowing/WinEventsX11.cpp | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/xbmc/windowing/WinEventsX11.cpp b/xbmc/windowing/WinEventsX11.cpp +index 2ec9b6f..4ed978c 100644 +--- a/xbmc/windowing/WinEventsX11.cpp ++++ b/xbmc/windowing/WinEventsX11.cpp +@@ -506,14 +506,16 @@ bool CWinEventsX11::MessagePump() + + case EnterNotify: + { +- g_Windowing.NotifyMouseCoverage(true); ++ if (xevent.xcrossing.mode == NotifyNormal) ++ g_Windowing.NotifyMouseCoverage(true); + break; + } + + // lose mouse coverage + case LeaveNotify: + { +- g_Windowing.NotifyMouseCoverage(false); ++ if (xevent.xcrossing.mode == NotifyNormal) ++ g_Windowing.NotifyMouseCoverage(false); + g_Mouse.SetActive(false); + break; + } +-- +1.8.1.6 + + +From 9fb2fac7ec44e7a2d6259c9f2253a04e159de49d Mon Sep 17 00:00:00 2001 +From: xbmc +Date: Sun, 16 Jun 2013 13:23:19 +0200 +Subject: [PATCH 107/108] renderer: delete fence on uninit + +--- + xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +index c663423..858d39d 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +@@ -1164,7 +1164,12 @@ void CLinuxRendererGL::UnInit() + + // YV12 textures + for (int i = 0; i < NUM_BUFFERS; ++i) ++ { + (this->*m_textureDelete)(i); ++ if (m_buffers[i].fence) ++ glDeleteSync(m_buffers[i].fence); ++ m_buffers[i].fence = None; ++ } + + // cleanup framebuffer object if it was in use + m_fbo.fbo.Cleanup(); +-- +1.8.1.6 + + +From c30c705ea7199cb94c4709d41f0500f60d0c7345 Mon Sep 17 00:00:00 2001 +From: Rainer Hochecker +Date: Sun, 16 Jun 2013 14:28:01 +0200 +Subject: [PATCH 108/108] renderer: limit fence to vdpau + +--- + xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 15 +++++++++------ + 1 file changed, 9 insertions(+), 6 deletions(-) + +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +index 858d39d..bb198f3 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +@@ -1254,14 +1254,17 @@ void CLinuxRendererGL::Render(DWORD flags, int renderBuffer) + VerifyGLState(); + } + +- // set fence in order to determine when buffer is ready for reuse +- // this is the case when the gl has finished processing +- if(m_buffers[renderBuffer].fence) ++ if (m_format == RENDER_FMT_VDPAU || m_format == RENDER_FMT_VDPAU_420) + { +- glDeleteSync(m_buffers[renderBuffer].fence); +- m_buffers[renderBuffer].fence = None; ++ // set fence in order to determine when buffer is ready for reuse ++ // this is the case when the gl has finished processing ++ if(m_buffers[renderBuffer].fence) ++ { ++ glDeleteSync(m_buffers[renderBuffer].fence); ++ m_buffers[renderBuffer].fence = None; ++ } ++ m_buffers[renderBuffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } +- m_buffers[renderBuffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + + void CLinuxRendererGL::RenderSinglePass(int index, int field) +-- +1.8.1.6 + diff --git a/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch b/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch deleted file mode 100644 index 60f3dd4b6a..0000000000 --- a/packages/mediacenter/xbmc/patches/6aa6247/xbmc-995.20-segfault_fix.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 7dcf55c9560172db7e9daee2c20413cd8c9549dd Mon Sep 17 00:00:00 2001 -From: xbmc -Date: Sun, 16 Jun 2013 13:23:19 +0200 -Subject: [PATCH] renderer: delete fence on uninit - ---- - xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp -index c663423..858d39d 100644 ---- a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp -+++ b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp -@@ -1164,7 +1164,12 @@ void CLinuxRendererGL::UnInit() - - // YV12 textures - for (int i = 0; i < NUM_BUFFERS; ++i) -+ { - (this->*m_textureDelete)(i); -+ if (m_buffers[i].fence) -+ glDeleteSync(m_buffers[i].fence); -+ m_buffers[i].fence = None; -+ } - - // cleanup framebuffer object if it was in use - m_fbo.fbo.Cleanup(); --- -1.8.1.6 - From 412617e37747184af3a1809a25fa3d609356c179 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 06:46:49 +0200 Subject: [PATCH 07/15] xbmc-pvr-addons (gotham): update to xbmc-pvr-addons-b84a88f Signed-off-by: Stephan Raue --- packages/mediacenter/xbmc-pvr-addons/meta | 2 +- .../ed5ff3c/xbmc-pvr-addons-PR194.patch | 1927 ----------------- 2 files changed, 1 insertion(+), 1928 deletions(-) delete mode 100644 packages/mediacenter/xbmc-pvr-addons/patches/ed5ff3c/xbmc-pvr-addons-PR194.patch diff --git a/packages/mediacenter/xbmc-pvr-addons/meta b/packages/mediacenter/xbmc-pvr-addons/meta index dd6cdd8fbf..98d13eb4aa 100644 --- a/packages/mediacenter/xbmc-pvr-addons/meta +++ b/packages/mediacenter/xbmc-pvr-addons/meta @@ -21,7 +21,7 @@ PKG_NAME="xbmc-pvr-addons" PKG_VERSION="frodo-d37ec1d" if [ "$XBMC" = "master" ]; then - PKG_VERSION="fb8d0d9" + PKG_VERSION="b84a88f" fi PKG_REV="1" PKG_ARCH="any" diff --git a/packages/mediacenter/xbmc-pvr-addons/patches/ed5ff3c/xbmc-pvr-addons-PR194.patch b/packages/mediacenter/xbmc-pvr-addons/patches/ed5ff3c/xbmc-pvr-addons-PR194.patch deleted file mode 100644 index 37876fdab4..0000000000 --- a/packages/mediacenter/xbmc-pvr-addons/patches/ed5ff3c/xbmc-pvr-addons-PR194.patch +++ /dev/null @@ -1,1927 +0,0 @@ -From aa406f296d2e9294a508a9cecc17ff586aea10a9 Mon Sep 17 00:00:00 2001 -From: Christian Fetzer -Date: Wed, 17 Apr 2013 20:03:14 +0200 -Subject: [PATCH] [mythtv-cmyth] Release v1.7.11 - ---- - addons/pvr.mythtv.cmyth/addon/addon.xml.in | 2 +- - addons/pvr.mythtv.cmyth/addon/changelog.txt | 7 ++ - addons/pvr.mythtv.cmyth/src/client.h | 6 ++ - .../src/cppmyth/MythConnection.cpp | 12 +-- - .../pvr.mythtv.cmyth/src/cppmyth/MythConnection.h | 3 +- - .../src/cppmyth/MythEventHandler.cpp | 4 +- - .../src/cppmyth/MythEventHandler.h | 18 ++-- - .../pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp | 2 +- - .../pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp | 7 ++ - .../pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h | 3 + - addons/pvr.mythtv.cmyth/src/fileOps.cpp | 2 +- - addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp | 25 +++-- - lib/cmyth/include/cmyth/cmyth.h | 6 +- - lib/cmyth/libcmyth/bookmark.c | 8 +- - lib/cmyth/libcmyth/cmyth_local.h | 11 +-- - lib/cmyth/libcmyth/cmyth_msc.h | 4 +- - lib/cmyth/libcmyth/commbreak.c | 12 +-- - lib/cmyth/libcmyth/connection.c | 108 ++++++--------------- - lib/cmyth/libcmyth/file.c | 54 +++++++---- - lib/cmyth/libcmyth/input.c | 4 +- - lib/cmyth/libcmyth/livetv.c | 22 +++-- - lib/cmyth/libcmyth/proginfo.c | 53 ++++------ - lib/cmyth/libcmyth/proglist.c | 10 +- - lib/cmyth/libcmyth/recorder.c | 52 +++++----- - lib/cmyth/libcmyth/ringbuf.c | 16 +-- - lib/cmyth/libcmyth/socket.c | 18 ++-- - lib/cmyth/libcmyth/storagegroup.c | 8 +- - lib/cmyth/libcmyth/timestamp.c | 78 ++++++++++++--- - 28 files changed, 294 insertions(+), 261 deletions(-) - -diff --git a/addons/pvr.mythtv.cmyth/addon/addon.xml.in b/addons/pvr.mythtv.cmyth/addon/addon.xml.in -index 919de5c..596ffb5 100644 ---- a/addons/pvr.mythtv.cmyth/addon/addon.xml.in -+++ b/addons/pvr.mythtv.cmyth/addon/addon.xml.in -@@ -1,7 +1,7 @@ - - - -diff --git a/addons/pvr.mythtv.cmyth/addon/changelog.txt b/addons/pvr.mythtv.cmyth/addon/changelog.txt -index bd2a35e..dac1f05 100644 ---- a/addons/pvr.mythtv.cmyth/addon/changelog.txt -+++ b/addons/pvr.mythtv.cmyth/addon/changelog.txt -@@ -1,3 +1,10 @@ -+v1.7.11 -+- Fixed crash when setting up Live TV (in rare cases) -+- Fixed issue with not updated recording list (Daylight Saving Time) -+- Fixed issue with empty recording list (caused by '[' character) -+- Fixed playback of recordings made by a slave backend -+- Removed 'Failed to connect to MythTV Backend' notification -+ - v1.7.10 - - Get last played position for recordings from the server - -diff --git a/addons/pvr.mythtv.cmyth/src/client.h b/addons/pvr.mythtv.cmyth/src/client.h -index a577752..5b98095 100644 ---- a/addons/pvr.mythtv.cmyth/src/client.h -+++ b/addons/pvr.mythtv.cmyth/src/client.h -@@ -36,6 +36,12 @@ - #define strdup _strdup // # strdup is POSIX, _strdup should be used instead - #endif - -+#define TCP_RCV_BUF_CONTROL_SIZE 128000 // Inherited from MythTV's MythSocket class -+#define RCV_BUF_CONTROL_SIZE 32000 // Buffer size to parse backend response from control connection -+#define TCP_RCV_BUF_DATA_SIZE 65536 // TCP buffer for video stream (best performance) -+#define RCV_BUF_DATA_SIZE 64 // Buffer size to parse backend response from data control connection -+#define RCV_BUF_IMAGE_SIZE 32000 // Buffer size to download artworks -+ - #define LIVETV_CONFLICT_STRATEGY_HASLATER 0 - #define LIVETV_CONFLICT_STRATEGY_STOPTV 1 - #define LIVETV_CONFLICT_STRATEGY_CANCELREC 2 -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp -index 55f94ba..fd46a18 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp -@@ -77,7 +77,7 @@ - , m_port(port) - , m_pEventHandler(NULL) - { -- cmyth_conn_t connection = cmyth_conn_connect_ctrl(const_cast(server.c_str()), port, 64 * 1024, 16 * 1024); -+ cmyth_conn_t connection = cmyth_conn_connect_ctrl(const_cast(server.c_str()), port, RCV_BUF_CONTROL_SIZE, TCP_RCV_BUF_CONTROL_SIZE); - *m_conn_t = connection; - } - -@@ -249,13 +249,13 @@ MythProgramInfo MythConnection::GetRecordedProgram(const CStdString &basename) - return retval; - } - --MythProgramInfo MythConnection::GetRecordedProgram(int chanid, time_t recstartts) -+MythProgramInfo MythConnection::GetRecordedProgram(int chanid, const MythTimestamp &recstartts) - { - MythProgramInfo retval; - cmyth_proginfo_t prog = NULL; -- if (chanid > 0 && recstartts > 0) -+ if (chanid > 0 && !recstartts.IsNull()) - { -- CMYTH_CONN_CALL_REF(prog, prog == NULL, cmyth_proginfo_get_from_timeslot(*m_conn_t, chanid, recstartts)); -+ CMYTH_CONN_CALL_REF(prog, prog == NULL, cmyth_proginfo_get_from_timeslot(*m_conn_t, chanid, *recstartts.m_timestamp_t)); - if (prog) { - retval = MythProgramInfo(prog); - } -@@ -334,7 +334,7 @@ MythFile MythConnection::ConnectFile(MythProgramInfo &recording) - // When file is not NULL doesn't need to mean there is no more connection, - // so always check after calling cmyth_conn_connect_file if still connected to control socket. - cmyth_file_t file = NULL; -- CMYTH_CONN_CALL_REF(file, true, cmyth_conn_connect_file(*recording.m_proginfo_t, *m_conn_t, 64 * 1024, 16 * 1024)); -+ CMYTH_CONN_CALL_REF(file, true, cmyth_conn_connect_file(*recording.m_proginfo_t, *m_conn_t, RCV_BUF_DATA_SIZE, TCP_RCV_BUF_DATA_SIZE)); - MythFile retval = MythFile(file, *this); - return retval; - } -@@ -342,7 +342,7 @@ MythFile MythConnection::ConnectFile(MythProgramInfo &recording) - MythFile MythConnection::ConnectPath(const CStdString &filename, const CStdString &storageGroup) - { - cmyth_file_t file = NULL; -- CMYTH_CONN_CALL_REF(file, file == NULL, cmyth_conn_connect_path(const_cast(filename.c_str()), *m_conn_t, 64 * 1024, 64 * 1024, const_cast(storageGroup.c_str()))); -+ CMYTH_CONN_CALL_REF(file, file == NULL, cmyth_conn_connect_path(const_cast(filename.c_str()), *m_conn_t, RCV_BUF_DATA_SIZE, TCP_RCV_BUF_DATA_SIZE, const_cast(storageGroup.c_str()))); - return MythFile(file, *this); - } - -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h -index 022df6e..c1fe008 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h -@@ -35,6 +35,7 @@ - class MythEventHandler; - class MythRecordingRule; - class MythStorageGroupFile; -+class MythTimestamp; - - template class MythPointer; - template class MythPointerThreadSafe; -@@ -75,7 +76,7 @@ class MythConnection - bool DeleteRecording(MythProgramInfo &recording); - ProgramInfoMap GetRecordedPrograms(); - MythProgramInfo GetRecordedProgram(const CStdString &basename); -- MythProgramInfo GetRecordedProgram(int chanid, time_t recstartts); -+ MythProgramInfo GetRecordedProgram(int chanid, const MythTimestamp &recstartts); - - // Timers - ProgramInfoMap GetPendingPrograms(); -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp -index 8bd4d31..e3ac82c 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp -@@ -109,7 +109,7 @@ class MythEventHandler::MythEventHandlerPrivate : public CThread, public CMutex - , m_hang(false) - , m_recordingChangeEventList() - { -- *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), port, 64 * 1024, 16 * 1024); -+ *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), port, RCV_BUF_CONTROL_SIZE, TCP_RCV_BUF_CONTROL_SIZE); - } - - MythEventHandler::MythEventHandlerPrivate::~MythEventHandlerPrivate() -@@ -460,7 +460,7 @@ void MythEventHandler::MythEventHandlerPrivate::RetryConnect() - usleep(999999); - ref_release(*m_conn_t); - *m_conn_t = NULL; -- *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), m_port, 64 * 1024, 16 * 1024); -+ *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), m_port, RCV_BUF_CONTROL_SIZE, TCP_RCV_BUF_CONTROL_SIZE); - - if (*m_conn_t == NULL) - XBMC->Log(LOG_NOTICE, "%s - Could not connect client to event socket", __FUNCTION__); -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h -index 359964b..b2ffcc7 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h -@@ -78,21 +78,16 @@ class MythEventHandler - class RecordingChangeEvent - { - public: -- RecordingChangeEvent(RecordingChangeType type, unsigned int chanid, char *recstartts) -+ RecordingChangeEvent(RecordingChangeType type, unsigned int chanid, const CStdString &recstartts) - : m_type(type) - , m_channelID(chanid) -- , m_recordStartTimeSlot(0) - { -- if (recstartts) { -- MythTimestamp time(recstartts, false); -- m_recordStartTimeSlot = time.UnixTime(); -- } -+ m_recordingStartTimeSlot = MythTimestamp(recstartts, false); - } - - RecordingChangeEvent(RecordingChangeType type, const MythProgramInfo &prog) - : m_type(type) - , m_channelID(0) -- , m_recordStartTimeSlot(0) - , m_prog(prog) - { - } -@@ -100,20 +95,19 @@ class MythEventHandler - RecordingChangeEvent(RecordingChangeType type) - : m_type(type) - , m_channelID(0) -- , m_recordStartTimeSlot(0) - { - } - - RecordingChangeType Type() const { return m_type; } - unsigned int ChannelID() const { return m_channelID; }; -- time_t RecordingStartTimeslot() const { return m_recordStartTimeSlot; } -+ MythTimestamp RecordingStartTimeslot() const { return m_recordingStartTimeSlot; } - MythProgramInfo Program() const { return m_prog; } - - private: - RecordingChangeType m_type; -- unsigned int m_channelID; // ADD and DELETE -- time_t m_recordStartTimeSlot; // ADD and DELETE -- MythProgramInfo m_prog; // UPDATE -+ unsigned int m_channelID; // ADD and DELETE -+ MythTimestamp m_recordingStartTimeSlot; // ADD and DELETE -+ MythProgramInfo m_prog; // UPDATE - }; - - bool HasRecordingChangeEvent() const; -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp -index 0df71af..81cebea 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp -@@ -159,7 +159,7 @@ bool MythRecorder::SpawnLiveTV(MythChannel &channel) - // Check channel - *m_liveChainUpdated = 0; - cmyth_recorder_t recorder = NULL; -- recorder = cmyth_spawn_live_tv(*m_recorder_t, 64*1024, 64*1024, MythRecorder::prog_update_callback, &pErr, const_cast(channel.Number().c_str())); -+ recorder = cmyth_spawn_live_tv(*m_recorder_t, RCV_BUF_DATA_SIZE, TCP_RCV_BUF_DATA_SIZE, MythRecorder::prog_update_callback, &pErr, const_cast(channel.Number().c_str())); - - if (recorder && pErr == NULL) { - *m_recorder_t = recorder; -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp -index 8518c63..f8ec968 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp -@@ -94,3 +94,10 @@ CStdString MythTimestamp::DisplayString(bool use12hClock) - bool succeded=cmyth_timestamp_to_display_string(time, *m_timestamp_t, use12hClock) == 0; - return succeded ? CStdString(time) : CStdString(""); - } -+ -+CStdString MythTimestamp::NumString() -+{ -+ char time[15]; -+ bool succeded = cmyth_timestamp_to_numstring(time, *m_timestamp_t) == 0; -+ return succeded ? CStdString(time) : CStdString(""); -+} -\ No newline at end of file -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h -index ecff511..a4ee3a5 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h -@@ -32,6 +32,8 @@ - class MythTimestamp - { - public: -+ friend class MythConnection; -+ - MythTimestamp(); - MythTimestamp(cmyth_timestamp_t cmyth_timestamp); - MythTimestamp(CStdString time, bool datetime); -@@ -50,6 +52,7 @@ class MythTimestamp - CStdString String(); - CStdString IsoString(); - CStdString DisplayString(bool use12hClock); -+ CStdString NumString(); - - private: - boost::shared_ptr > m_timestamp_t; -diff --git a/addons/pvr.mythtv.cmyth/src/fileOps.cpp b/addons/pvr.mythtv.cmyth/src/fileOps.cpp -index ec7089b..b51e6d8 100644 ---- a/addons/pvr.mythtv.cmyth/src/fileOps.cpp -+++ b/addons/pvr.mythtv.cmyth/src/fileOps.cpp -@@ -298,7 +298,7 @@ bool FileOps::CacheFile(const CStdString &localFilename, MythFile &source) - unsigned long long totalLength = source.Length(); - unsigned long long totalRead = 0; - -- const long buffersize = 32768; -+ const long buffersize = RCV_BUF_IMAGE_SIZE; - char* buffer = new char[buffersize]; - - while (totalRead < totalLength) -diff --git a/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp b/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp -index cedf9a1..2916bce 100644 ---- a/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp -+++ b/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp -@@ -186,7 +186,6 @@ bool PVRClientMythTV::Connect() - if (!m_con.IsConnected()) - { - XBMC->Log(LOG_ERROR,"Failed to connect to MythTV backend on %s:%d", g_szMythHostname.c_str(), g_iMythPort); -- XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30300)); - return false; - } - -@@ -622,7 +621,7 @@ void PVRClientMythTV::EventUpdateRecordings() - } - } - else -- XBMC->Log(LOG_ERROR, "%s - Add recording failed for %u %ld", __FUNCTION__, event.ChannelID(), event.RecordingStartTimeslot()); -+ XBMC->Log(LOG_ERROR, "%s - Add recording failed for %u %s", __FUNCTION__, event.ChannelID(), event.RecordingStartTimeslot().NumString().c_str()); - break; - } - case MythEventHandler::CHANGE_UPDATE: -@@ -1389,7 +1388,10 @@ bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel) - m_pEventHandler->SetRecorder(m_rec); - - if (m_rec.SpawnLiveTV((*channelByNumberIt).second)) -+ { -+ XBMC->Log(LOG_DEBUG, "%s - Done", __FUNCTION__); - return true; -+ } - } - } - } -@@ -1407,19 +1409,14 @@ bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel) - - XBMC->Log(LOG_ERROR,"%s - Failed to open live stream", __FUNCTION__); - XBMC->QueueNotification(QUEUE_WARNING, XBMC->GetLocalizedString(30305)); // Channel unavailable -- -- return false; - } - else - { -- if (g_bExtraDebug) -- XBMC->Log(LOG_DEBUG,"%s - Done", __FUNCTION__); -- -- return true; -+ XBMC->Log(LOG_ERROR, "%s - Live stream is already opened. recorder: %lu", __FUNCTION__, m_rec.ID()); - } -+ return false; - } - -- - void PVRClientMythTV::CloseLiveStream() - { - if (g_bExtraDebug) -@@ -1610,7 +1607,14 @@ bool PVRClientMythTV::OpenRecordedStream(const PVR_RECORDING &recording) - // Enable playback mode: Keep quiet on connection - m_pEventHandler->EnablePlayback(); - -- m_file = m_con.ConnectFile(it->second); -+ // Currently we only request the stream from the master backend. -+ // Future implementations could request the stream from slaves if not available on the master. -+ -+ // Create dedicated control connection for file playback; smart pointer deletes it when file gets deleted. -+ MythConnection fileControlConnection(g_szMythHostname, g_iMythPort); -+ if (!fileControlConnection.IsNull()) -+ m_file = fileControlConnection.ConnectFile(it->second); -+ - m_pEventHandler->SetRecordingListener(recording.strRecordingId, m_file); - - // Resume fileOps -@@ -1637,6 +1641,7 @@ void PVRClientMythTV::CloseRecordedStream() - XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__); - - m_file = MythFile(); -+ m_pEventHandler->SetRecordingListener("", m_file); - - m_pEventHandler->DisablePlayback(); - -diff --git a/lib/cmyth/include/cmyth/cmyth.h b/lib/cmyth/include/cmyth/cmyth.h -index 9de2d59..e790b65 100644 ---- a/lib/cmyth/include/cmyth/cmyth.h -+++ b/lib/cmyth/include/cmyth/cmyth.h -@@ -630,7 +630,7 @@ extern cmyth_recorder_t cmyth_spawn_live_tv(cmyth_recorder_t rec, - char ** err, char * channame); - - extern cmyth_recorder_t cmyth_livetv_chain_setup(cmyth_recorder_t old_rec, -- int32_t tcp_rcvbuf, -+ uint32_t buflen, int32_t tcp_rcvbuf, - void (*prog_update_callback)(cmyth_proginfo_t)); - - extern int32_t cmyth_livetv_get_block(cmyth_recorder_t rec, char *buf, -@@ -730,7 +730,7 @@ extern int cmyth_timestamp_to_display_string(char *str, cmyth_timestamp_t ts, - - extern int cmyth_datetime_to_string(char *str, cmyth_timestamp_t ts); - --extern cmyth_timestamp_t cmyth_datetime_from_string(char *str); -+extern int cmyth_timestamp_to_numstring(char *str, cmyth_timestamp_t ts); - - extern int cmyth_timestamp_compare(cmyth_timestamp_t ts1, - cmyth_timestamp_t ts2); -@@ -837,7 +837,7 @@ extern cmyth_proginfo_t cmyth_proginfo_get_from_basename(cmyth_conn_t control, - */ - extern cmyth_proginfo_t cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, - uint32_t chanid, -- time_t recstartts); -+ const cmyth_timestamp_t recstartts); - - /** - * Retrieve the title of a program. -diff --git a/lib/cmyth/libcmyth/bookmark.c b/lib/cmyth/libcmyth/bookmark.c -index dde35f6..12d534d 100644 ---- a/lib/cmyth/libcmyth/bookmark.c -+++ b/lib/cmyth/libcmyth/bookmark.c -@@ -41,7 +41,7 @@ int64_t cmyth_get_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog) - } - sprintf(buf,"%s %"PRIu32" %s","QUERY_BOOKMARK",prog->proginfo_chanId, - start_ts_dt); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex);; - if ((err = cmyth_send_message(conn,buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -66,7 +66,7 @@ int64_t cmyth_get_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog) - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return ret; - } - -@@ -94,7 +94,7 @@ int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, int64_t bookmar - sprintf(buf, "SET_BOOKMARK %"PRIu32" %s %"PRId32" %"PRId32, prog->proginfo_chanId, - start_ts_dt, (int32_t)(bookmark >> 32), (int32_t)(bookmark & 0xffffffff)); - } -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex);; - if ((ret = cmyth_send_message(conn,buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -106,6 +106,6 @@ int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, int64_t bookmar - __FUNCTION__); - } - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return ret; - } -diff --git a/lib/cmyth/libcmyth/cmyth_local.h b/lib/cmyth/libcmyth/cmyth_local.h -index 42d6477..619db48 100644 ---- a/lib/cmyth/libcmyth/cmyth_local.h -+++ b/lib/cmyth/libcmyth/cmyth_local.h -@@ -40,10 +40,6 @@ - - #if defined(_MSC_VER) - #include "cmyth_msc.h" --//#define PTHREAD_MUTEX_INITIALIZER NULL; --#define PTHREAD_MUTEX_INITIALIZER InitializeCriticalSection(&mutex); --//typedef void* pthread_mutex_t; --typedef CRITICAL_SECTION pthread_mutex_t; - #else - #include - #include -@@ -58,9 +54,6 @@ - #define closesocket(fd) close(fd) - #endif /* _MSC_VER */ - --#define mutex __cmyth_mutex --extern pthread_mutex_t mutex; -- - /* - * Some useful constants - */ -@@ -69,6 +62,7 @@ - #define CMYTH_INT16_LEN (sizeof("-65536") - 1) - #define CMYTH_INT8_LEN (sizeof("-256") - 1) - #define CMYTH_TIMESTAMP_LEN (sizeof("YYYY-MM-DDTHH:MM:SS") - 1) -+#define CMYTH_TIMESTAMP_NUMERIC_LEN (sizeof("YYYYMMDDHHMMSS") - 1) - #define CMYTH_DATESTAMP_LEN (sizeof("YYYY-MM-DD") - 1) - #define CMYTH_UTC_LEN (sizeof("1240120680") - 1) - #define CMYTH_COMMBREAK_START 4 -@@ -91,6 +85,7 @@ struct cmyth_conn { - char * server; /**< hostname of server */ - uint16_t port; /**< port of server */ - cmyth_conn_ann_t conn_ann; /**< connection announcement */ -+ pthread_mutex_t conn_mutex; - }; - - /* Sergio: Added to support new livetv protocol */ -@@ -104,6 +99,7 @@ struct cmyth_livetv_chain { - char **chain_urls; - cmyth_file_t *chain_files; /* File pointers for the urls */ - volatile int8_t livetv_watch; /* JLB: Manage program breaks */ -+ int32_t livetv_buflen; - int32_t livetv_tcp_rcvbuf; - int32_t livetv_block_len; - }; -@@ -255,6 +251,7 @@ struct cmyth_proginfo { - struct cmyth_proglist { - cmyth_proginfo_t *proglist_list; - int proglist_count; -+ pthread_mutex_t proglist_mutex; - }; - - /* -diff --git a/lib/cmyth/libcmyth/cmyth_msc.h b/lib/cmyth/libcmyth/cmyth_msc.h -index 76cea08..28050fe 100644 ---- a/lib/cmyth/libcmyth/cmyth_msc.h -+++ b/lib/cmyth/libcmyth/cmyth_msc.h -@@ -36,11 +36,11 @@ - #pragma warning(disable:4267) - #pragma warning(disable:4996) - -+#define pthread_mutex_init(a, b) InitializeCriticalSection(a) -+#define pthread_mutex_destroy(a) DeleteCriticalSection(a) - #define pthread_mutex_lock(a) EnterCriticalSection(a) - #define pthread_mutex_unlock(a) LeaveCriticalSection(a) --#define PTHREAD_MUTEX_INITIALIZER InitializeCriticalSection(&mutex); - typedef CRITICAL_SECTION pthread_mutex_t; --extern pthread_mutex_t mutex; - - #define SHUT_RDWR SD_BOTH - -diff --git a/lib/cmyth/libcmyth/commbreak.c b/lib/cmyth/libcmyth/commbreak.c -index 4bf66b2..2a28265 100644 ---- a/lib/cmyth/libcmyth/commbreak.c -+++ b/lib/cmyth/libcmyth/commbreak.c -@@ -110,7 +110,7 @@ - - sprintf(buf,"%s %"PRIu32" %ld", "QUERY_COMMBREAK", prog->proginfo_chanId, - (long)cmyth_timestamp_to_unixtime(prog->proginfo_rec_start_ts)); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if ((err = cmyth_send_message(conn, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -134,7 +134,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return breaklist; - } - -@@ -156,7 +156,7 @@ - - sprintf(buf,"%s %"PRIu32" %ld", "QUERY_CUTLIST", prog->proginfo_chanId, - (long)cmyth_timestamp_to_unixtime(prog->proginfo_rec_start_ts)); -- -+ pthread_mutex_lock(&conn->conn_mutex); - if ((err = cmyth_send_message(conn, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -180,7 +180,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return breaklist; - } - -@@ -287,7 +287,7 @@ int cmyth_rcv_commbreaklist(cmyth_conn_t conn, int *err, - int r; - - start_ts_dt = cmyth_timestamp_to_unixtime(prog->proginfo_rec_start_ts); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if ((r=cmyth_mysql_get_commbreak_list(db, prog->proginfo_chanId, start_ts_dt, breaklist, conn->conn_version)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_mysql_get_commbreak_list() failed (%d)\n", -@@ -302,6 +302,6 @@ int cmyth_rcv_commbreaklist(cmyth_conn_t conn, int *err, - breaklist->commbreak_count = 0; - } - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return breaklist; - } -diff --git a/lib/cmyth/libcmyth/connection.c b/lib/cmyth/libcmyth/connection.c -index 24a2145..9abe834 100644 ---- a/lib/cmyth/libcmyth/connection.c -+++ b/lib/cmyth/libcmyth/connection.c -@@ -33,31 +33,6 @@ - static char * cmyth_conn_get_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting); - static int cmyth_conn_set_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting, const char* value); - --#ifdef _MSC_VER --CRITICAL_SECTION mutex; -- --BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) --{ -- switch( ul_reason_for_call ) -- { -- case DLL_PROCESS_ATTACH: -- InitializeCriticalSection(&mutex); -- break; -- /*case DLL_THREAD_ATTACH: -- ... -- case DLL_THREAD_DETACH: -- ...*/ -- case DLL_PROCESS_DETACH: -- DeleteCriticalSection(&mutex); -- break; -- } -- return TRUE; --} -- --#else --pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; --#endif -- - typedef struct { - unsigned int version; - char token[14]; // up to 13 chars used in v74 + the terminating NULL character -@@ -106,7 +81,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return; - } - if (!conn->conn_hang && conn->conn_ann != ANN_NONE) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - /* - * Try to shut down the connection. Can't do much -@@ -118,7 +93,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - __FUNCTION__, err); - } - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - } - } - -@@ -146,6 +121,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - { - free( conn->server ); - } -+ pthread_mutex_destroy(&conn->conn_mutex); - cmyth_dbg(CMYTH_DBG_DEBUG, "%s }\n", __FUNCTION__); - } - -@@ -186,6 +162,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - ret->server = NULL; - ret->port = 0; - ret->conn_ann = ANN_NONE; -+ pthread_mutex_init(&ret->conn_mutex, NULL); - cmyth_dbg(CMYTH_DBG_DEBUG, "%s }\n", __FUNCTION__); - return ret; - } -@@ -258,13 +235,12 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - - temp = tcp_rcvbuf; - size = sizeof(temp); -+ cmyth_dbg(CMYTH_DBG_DEBUG, "%s: setting socket option SO_RCVBUF to %d", __FUNCTION__, tcp_rcvbuf); - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, size); - if(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, &size)) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: could not get rcvbuf from socket(%d)\n", - __FUNCTION__, errno); -- temp = tcp_rcvbuf; - } -- tcp_rcvbuf = temp; - - if (getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf), NI_NUMERICHOST)) { - strcpy(namebuf, "[unknown]"); -@@ -376,13 +352,12 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - - temp = conn->conn_tcp_rcvbuf; - size = sizeof(temp); -+ cmyth_dbg(CMYTH_DBG_DEBUG, "%s: setting socket option SO_RCVBUF to %d", __FUNCTION__, conn->conn_tcp_rcvbuf); - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, size); - if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, &size)) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: could not get rcvbuf from socket(%d)\n", - __FUNCTION__, errno); -- temp = conn->conn_tcp_rcvbuf; - } -- conn->conn_tcp_rcvbuf = temp; - - if (getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf), NI_NUMERICHOST)) { - strcpy(namebuf, "[unknown]"); -@@ -881,7 +856,6 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - { - cmyth_conn_t conn = NULL; - char *announcement = NULL; -- char *myth_host = NULL; - char reply[16]; - int err = 0; - int count = 0; -@@ -905,33 +879,11 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - __FUNCTION__); - goto shut; - } -- cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", -- __FUNCTION__); -- if (control->conn_version >= 17) { -- myth_host = cmyth_conn_get_setting(control, prog->proginfo_host, -- "BackendServerIP"); -- if (myth_host && (strcmp(myth_host, "-1") == 0)) { -- ref_release(myth_host); -- myth_host = NULL; -- } -- } -- if (!myth_host) { -- cmyth_dbg(CMYTH_DBG_PROTO, -- "%s: BackendServerIP setting not found. Using proginfo_host: %s\n", -- __FUNCTION__, prog->proginfo_host); -- myth_host = ref_alloc(strlen(prog->proginfo_host) + 1); -- strcpy(myth_host, prog->proginfo_host); -- } -- conn = cmyth_connect(myth_host, prog->proginfo_port, -- buflen, tcp_rcvbuf); -- cmyth_dbg(CMYTH_DBG_PROTO, -- "%s: done connecting data connection, conn = %d\n", -- __FUNCTION__, conn); -+ cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", __FUNCTION__); -+ conn = cmyth_connect(control->server, control->port, buflen, tcp_rcvbuf); -+ cmyth_dbg(CMYTH_DBG_PROTO, "%s: done connecting data connection, conn = %d\n", __FUNCTION__, conn); - if (!conn) { -- cmyth_dbg(CMYTH_DBG_ERROR, -- "%s: cmyth_connect(%s, %"PRIu16", %"PRIu32") failed\n", -- __FUNCTION__, -- myth_host, prog->proginfo_port, buflen); -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %"PRIu16", %"PRIu32") failed\n", __FUNCTION__, control->server, control->port, buflen); - goto shut; - } - /* -@@ -1016,12 +968,10 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - ret->file_data = conn; - ret->file_id = file_id; - ret->file_length = file_length; -- ref_release(myth_host); - return ret; - - shut: - ref_release(conn); -- ref_release(myth_host); - return NULL; - } - -@@ -1390,7 +1340,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return NULL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((rec=cmyth_recorder_create()) == NULL) - goto fail; -@@ -1436,7 +1386,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - conn->conn_tcp_rcvbuf) < 0) - goto fail; - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return rec; - -@@ -1444,7 +1394,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - if (rec) - ref_release(rec); - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return NULL; - } -@@ -1483,7 +1433,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return NULL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((rec=cmyth_recorder_create()) == NULL) - goto fail; -@@ -1534,7 +1484,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - conn->conn_tcp_rcvbuf) < 0) - goto fail; - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return rec; - -@@ -1542,7 +1492,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - if (rec) - ref_release(rec); - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return NULL; - } -@@ -1563,7 +1513,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - if ((total == NULL) || (used == NULL)) - return -EINVAL; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if (control->conn_version >= 32) - { snprintf(msg, sizeof(msg), "QUERY_FREE_SPACE_SUMMARY"); } -@@ -1633,7 +1583,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - - return ret; - } -@@ -1675,7 +1625,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return -1; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - snprintf(msg, sizeof(msg), "GET_FREE_RECORDER_COUNT"); - if ((err = cmyth_send_message(conn, msg)) < 0) { -@@ -1704,7 +1654,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - ret = (int)c; - - err: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return ret; - } -@@ -1715,7 +1665,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - int count, err; - char* result = NULL; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if(conn->conn_version < 17) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support QUERY_HOSTNAME\n", - __FUNCTION__); -@@ -1758,7 +1708,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - buffer[sizeof(buffer)-1] = 0; - cmyth_dbg(CMYTH_DBG_ERROR, "%s: odd left over data %s\n", __FUNCTION__, buffer); - } -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - if(!strcmp("-1",result)) { - cmyth_dbg(CMYTH_DBG_PROTO, "%s: Failed to retrieve backend hostname.\n", -@@ -1768,7 +1718,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return result; - - err: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - if(result) - ref_release(result); - -@@ -1853,9 +1803,9 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - { - char* result = NULL; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - result = cmyth_conn_get_setting_unlocked(conn, hostname, setting); -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return result; - } -@@ -1900,9 +1850,9 @@ int cmyth_conn_set_setting(cmyth_conn_t conn, - { - int result; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - result = cmyth_conn_set_setting_unlocked(conn, hostname, setting, value); -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return result; - } -@@ -1970,7 +1920,7 @@ int cmyth_conn_set_setting(cmyth_conn_t conn, - } - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((err = cmyth_send_message(conn, msg)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -1987,6 +1937,6 @@ int cmyth_conn_set_setting(cmyth_conn_t conn, - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return err; - } -\ No newline at end of file -diff --git a/lib/cmyth/libcmyth/file.c b/lib/cmyth/libcmyth/file.c -index 2b2feb8..5aeaf02 100644 ---- a/lib/cmyth/libcmyth/file.c -+++ b/lib/cmyth/libcmyth/file.c -@@ -51,7 +51,7 @@ - return; - } - if (file->file_control) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - /* - * Try to shut down the file transfer. Can't do much -@@ -74,7 +74,7 @@ - goto fail; - } - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - ref_release(file->file_control); - } - if (file->closed_callback) { -@@ -409,7 +409,7 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - if(len > file->file_data->conn_tcp_rcvbuf) - len = file->file_data->conn_tcp_rcvbuf; -@@ -445,7 +445,7 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - ret = c; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - - return ret; - } -@@ -474,7 +474,7 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - int64_t - cmyth_file_seek(cmyth_file_t file, int64_t offset, int8_t whence) - { -- char msg[128]; -+ char msg[4096]; - int err; - int count; - int64_t c; -@@ -484,13 +484,26 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - if (file == NULL) - return -EINVAL; - -- if ((offset == 0) && (whence == WHENCE_CUR)) -- return file->file_pos; -- -- if ((offset == file->file_pos) && (whence == WHENCE_SET)) -- return file->file_pos; -+ if (whence == WHENCE_CUR) { -+ if (offset == 0) -+ return file->file_pos; -+ ret = file->file_pos + offset; -+ if (ret < 0 || ret > file->file_length) -+ goto inv; -+ } -+ if (whence == WHENCE_SET) { -+ if (offset == file->file_pos) -+ return file->file_pos; -+ if (offset < 0 || offset > file->file_length) -+ goto inv; -+ } -+ if (whence == WHENCE_END) { -+ ret = file->file_length - offset; -+ if (ret < 0 || ret > file->file_length) -+ goto inv; -+ } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - ret = 0; - while(file->file_pos < file->file_req) { -@@ -558,9 +571,14 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - ret = file->file_pos; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - - return ret; -+ -+inv: -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: seek out of range: file: %"PRIu32" pos: %"PRId64" length: %"PRId64" whence: %"PRId8" offset: %"PRId64, -+ __FUNCTION__, file->file_id, file->file_pos, file->file_length, whence, offset); -+ return -1; - } - - /* -@@ -600,7 +618,7 @@ int32_t cmyth_file_read(cmyth_file_t file, char *buf, int32_t len) - if(len > file->file_data->conn_tcp_rcvbuf) - len = file->file_data->conn_tcp_rcvbuf; - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock (&file->file_control->conn_mutex); - - /* make sure we have outstanding requests that fill the buffer that was called with */ - /* this way we should be able to saturate the network connection better */ -@@ -745,7 +763,7 @@ int32_t cmyth_file_read(cmyth_file_t file, char *buf, int32_t len) - - ret = (int32_t)(cur - buf); - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock (&file->file_control->conn_mutex); - return ret; - } - -@@ -780,7 +798,7 @@ int cmyth_file_is_open(cmyth_file_t file) - return -EINVAL; - } - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - snprintf (msg, sizeof (msg), - "QUERY_FILETRANSFER %"PRIu32"[]:[]IS_OPEN", -@@ -812,7 +830,7 @@ int cmyth_file_is_open(cmyth_file_t file) - if (ret == 0) - cmyth_dbg(CMYTH_DBG_ERROR, "%s: file transfer socket is closed\n", __FUNCTION__); - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - return ret; - } - -@@ -845,7 +863,7 @@ int cmyth_file_is_open(cmyth_file_t file) - return -EINVAL; - } - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_FILETRANSFER %"PRIu32"[]:[]SET_TIMEOUT[]:[]%"PRId32, -@@ -866,7 +884,7 @@ int cmyth_file_is_open(cmyth_file_t file) - ret = 0; - - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - return ret; - } - -diff --git a/lib/cmyth/libcmyth/input.c b/lib/cmyth/libcmyth/input.c -index 0ecc05a..64f0ed5 100644 ---- a/lib/cmyth/libcmyth/input.c -+++ b/lib/cmyth/libcmyth/input.c -@@ -114,7 +114,7 @@ - } - - sprintf(buf,"QUERY_RECORDER %"PRIu32"[]:[]GET_FREE_INPUTS", rec->rec_id); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - if ((err = cmyth_send_message(rec->rec_conn, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -138,7 +138,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - return inputlist; - } - -diff --git a/lib/cmyth/libcmyth/livetv.c b/lib/cmyth/libcmyth/livetv.c -index 02c303e..b458838 100644 ---- a/lib/cmyth/libcmyth/livetv.c -+++ b/lib/cmyth/libcmyth/livetv.c -@@ -121,6 +121,7 @@ static int cmyth_livetv_chain_add(cmyth_recorder_t rec, char * url, - ret->chain_files = NULL; - ret->progs = NULL; - ret->livetv_watch = 0; /* JLB: Manage program breaks */ -+ ret->livetv_buflen = 0; - ret->livetv_tcp_rcvbuf = 0; - ret->livetv_block_len = 0; - ref_set_destroy(ret, (ref_destroy_t)cmyth_livetv_chain_destroy); -@@ -443,7 +444,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - */ - - if (cmyth_livetv_chain_has_url(rec, url) == -1) { -- ft = cmyth_conn_connect_file(loc_prog, rec->rec_conn, 4096, rec->rec_livetv_chain->livetv_tcp_rcvbuf); -+ ft = cmyth_conn_connect_file(loc_prog, rec->rec_conn, rec->rec_livetv_chain->livetv_buflen, rec->rec_livetv_chain->livetv_tcp_rcvbuf); - if (!ft) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_conn_connect_file(%s) failed\n", -@@ -663,7 +664,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - * caller needs to do this on a failure. - */ - cmyth_recorder_t --cmyth_livetv_chain_setup(cmyth_recorder_t rec, int32_t tcp_rcvbuf, -+cmyth_livetv_chain_setup(cmyth_recorder_t rec, uint32_t buflen, int32_t tcp_rcvbuf, - void (*prog_update_callback)(cmyth_proginfo_t)) - { - -@@ -703,6 +704,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - } - - /* JLB: Set tcp receive buffer for the chain files */ -+ new_rec->rec_livetv_chain->livetv_buflen = buflen; - new_rec->rec_livetv_chain->livetv_tcp_rcvbuf = tcp_rcvbuf; - /* JLB: Manage program breaks. Switch OFF watch signal */ - new_rec->rec_livetv_chain->livetv_watch = 0; -@@ -713,7 +715,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - loc_prog->proginfo_pathname); - - if(cmyth_livetv_chain_has_url(new_rec, url) == -1) { -- ft = cmyth_conn_connect_file(loc_prog, new_rec->rec_conn, 4096, new_rec->rec_livetv_chain->livetv_tcp_rcvbuf); -+ ft = cmyth_conn_connect_file(loc_prog, new_rec->rec_conn, new_rec->rec_livetv_chain->livetv_buflen, new_rec->rec_livetv_chain->livetv_tcp_rcvbuf); - if (!ft) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_conn_connect_file(%s) failed\n", -@@ -736,7 +738,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - } - else { - /* now switch to the valid program */ -- cmyth_livetv_chain_switch(new_rec, 0); -+ cmyth_livetv_chain_switch_last(new_rec); - new_rec->rec_livetv_chain->chain_switch_on_create = 0; - } - } -@@ -820,7 +822,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - if (dir == 0) - return 1; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - ret = 0; - -@@ -841,9 +843,9 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - "%s: wait until livetv_watch is OFF\n", - __FUNCTION__); - for (i = 0; i < 4; i++) { -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - usleep(500000); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - if (rec->rec_livetv_chain->livetv_watch == 0) - break; - } -@@ -873,7 +875,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - - out: - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1131,7 +1133,7 @@ int32_t cmyth_livetv_chain_read(cmyth_recorder_t rec, char *buf, int32_t len) - whence = WHENCE_SET; - } - -- if (fp && cur >=0) -+ if (cur >=0 && cur < rec->rec_livetv_chain->chain_ct && fp) - { - if ((ret = cmyth_file_seek(fp, offset, whence)) >= 0) { - cur -= rec->rec_livetv_chain->chain_current; -@@ -1302,7 +1304,7 @@ int32_t cmyth_livetv_read(cmyth_recorder_t rec, char *buf, int32_t len) - break; - } - -- if ((rtrn = cmyth_livetv_chain_setup(rec, tcp_rcvbuf, -+ if ((rtrn = cmyth_livetv_chain_setup(rec, buflen, tcp_rcvbuf, - prog_update_callback)) == NULL) { - *err = "Failed to setup livetv."; - goto err; -diff --git a/lib/cmyth/libcmyth/proginfo.c b/lib/cmyth/libcmyth/proginfo.c -index ed7534b..33340cb 100644 ---- a/lib/cmyth/libcmyth/proginfo.c -+++ b/lib/cmyth/libcmyth/proginfo.c -@@ -581,7 +581,7 @@ - sprintf(buf, "DELETE_RECORDING 0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if ((err = cmyth_send_message(control, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -601,7 +601,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -663,7 +663,7 @@ - sprintf(buf, "FORGET_RECORDING 0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if ((err = cmyth_send_message(control, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -683,7 +683,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -745,7 +745,7 @@ - sprintf(buf, "STOP_RECORDING 0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if ((err = cmyth_send_message(control, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -765,7 +765,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -1477,7 +1477,7 @@ - sprintf(buf, "FILL_PROGRAM_INFO cmyth[]:[]0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - length = prog->proginfo_Length; - -@@ -1518,7 +1518,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -1730,7 +1730,7 @@ - cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); - return -1; - } -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if ((err = cmyth_send_message(conn, msg)) < 0) { - fprintf (stderr, "ERROR %d \n",err); - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -1740,7 +1740,7 @@ - count = cmyth_rcv_length(conn); - cmyth_rcv_proglist(conn, &err, prog, count); - prog_count=cmyth_proglist_get_count(prog); -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return prog_count; - } - -@@ -1766,7 +1766,7 @@ - * to enumerating all recordings - */ - if(control->conn_version >= 32 && strchr(basename, ' ') == NULL) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDING BASENAME %s", - basename); -@@ -1807,10 +1807,10 @@ - goto out; - } - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return prog; - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - if(prog) - ref_release(prog); - return NULL; -@@ -1846,14 +1846,13 @@ - } - - cmyth_proginfo_t --cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, uint32_t chanid, time_t recstartts) -+cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, uint32_t chanid, const cmyth_timestamp_t recstartts) - { - int err = 0; - int count, i; - char msg[4096]; - cmyth_proginfo_t prog = NULL; - cmyth_proglist_t list = NULL; -- cmyth_timestamp_t ts; - char time[15]; - - if (!control) { -@@ -1862,24 +1861,14 @@ - return NULL; - } - -- ts = cmyth_timestamp_from_unixtime(recstartts); -- if (!ts) { -- cmyth_dbg(CMYTH_DBG_ERROR, "%s: timestamp NULL\n", -- __FUNCTION__); -+ if ((err = cmyth_timestamp_to_numstring(time, recstartts)) < 0) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_timestamp_to_numstring() failed (%d)\n", -+ __FUNCTION__, err); - return NULL; - } - -- sprintf(time, -- "%4.4ld%2.2ld%2.2ld%2.2ld%2.2ld%2.2ld", -- ts->timestamp_year, -- ts->timestamp_month, -- ts->timestamp_day, -- ts->timestamp_hour, -- ts->timestamp_minute, -- ts->timestamp_second); -- - if(control->conn_version >= 32) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDING TIMESLOT %"PRIu32" %s", - chanid, time); -@@ -1920,10 +1909,10 @@ - goto out; - } - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return prog; - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - if(prog) - ref_release(prog); - return NULL; -@@ -1944,7 +1933,7 @@ - __FUNCTION__); - continue; - } -- if (cmyth_timestamp_compare(prog->proginfo_rec_start_ts, ts) != 0 || -+ if (cmyth_timestamp_compare(prog->proginfo_rec_start_ts, recstartts) != 0 || - prog->proginfo_chanId != chanid) { - ref_release(prog); - prog = NULL; -diff --git a/lib/cmyth/libcmyth/proglist.c b/lib/cmyth/libcmyth/proglist.c -index 96777df..63addb5 100644 ---- a/lib/cmyth/libcmyth/proglist.c -+++ b/lib/cmyth/libcmyth/proglist.c -@@ -62,6 +62,7 @@ - if (pl->proglist_list) { - free(pl->proglist_list); - } -+ pthread_mutex_destroy(&pl->proglist_mutex); - } - - /* -@@ -93,6 +94,7 @@ - - ret->proglist_list = NULL; - ret->proglist_count = 0; -+ pthread_mutex_init(&ret->proglist_mutex, NULL); - return ret; - } - -@@ -154,7 +156,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&pl->proglist_mutex); - - for (i=0; iproglist_count; i++) { - if (cmyth_proginfo_compare(prog, pl->proglist_list[i]) == 0) { -@@ -170,7 +172,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&pl->proglist_mutex); - - return ret; - } -@@ -237,7 +239,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((err = cmyth_send_message(conn, msg)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -282,7 +284,7 @@ - ret = 0; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return ret; - } -diff --git a/lib/cmyth/libcmyth/recorder.c b/lib/cmyth/libcmyth/recorder.c -index 6029ba7..1d78556 100644 ---- a/lib/cmyth/libcmyth/recorder.c -+++ b/lib/cmyth/libcmyth/recorder.c -@@ -180,7 +180,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]IS_RECORDING", - rec->rec_id); -@@ -205,7 +205,7 @@ - ret = c; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -242,7 +242,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]GET_FRAMERATE", - rec->rec_id); -@@ -269,7 +269,7 @@ - ret = 0; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -458,7 +458,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]CANCEL_NEXT_RECORDING[]:[]%"PRIu32 ,rec->rec_id, cancel == 1); - -@@ -475,7 +475,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -511,7 +511,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - sprintf(Buffer, "QUERY_RECORDER %"PRIu32"[]:[]PAUSE", rec->rec_id); - if ((ret=cmyth_send_message(rec->rec_conn, Buffer)) < 0) { -@@ -530,7 +530,7 @@ - ret = 0; - - err: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -624,7 +624,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]CHANGE_CHANNEL[]:[]%d", -@@ -658,7 +658,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -697,7 +697,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]SET_CHANNEL[]:[]%s", -@@ -731,7 +731,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -889,7 +889,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]CHECK_CHANNEL[]:[]%s", -@@ -910,7 +910,7 @@ - ret = 1; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -976,7 +976,7 @@ - __FUNCTION__); - goto out; - } -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - if(rec->rec_conn->conn_version >= 26) - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]GET_CURRENT_RECORDING", -@@ -1010,7 +1010,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return proginfo; - } -@@ -1106,7 +1106,7 @@ - - control = rec->rec_conn; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - t = time(NULL); - tm = localtime(&t); -@@ -1192,7 +1192,7 @@ - ret = 0; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1351,7 +1351,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]SPAWN_LIVETV", - rec->rec_id); -@@ -1373,7 +1373,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1395,7 +1395,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - - /* Get our own IP address */ -@@ -1436,7 +1436,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1454,7 +1454,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]STOP_LIVETV", - rec->rec_id); -@@ -1476,7 +1476,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1497,7 +1497,7 @@ - if(rec->rec_conn->conn_version >= 26) - return 0; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]DONE_RINGBUF", - rec->rec_id); -@@ -1519,7 +1519,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -diff --git a/lib/cmyth/libcmyth/ringbuf.c b/lib/cmyth/libcmyth/ringbuf.c -index 173c5a7..6686a54 100644 ---- a/lib/cmyth/libcmyth/ringbuf.c -+++ b/lib/cmyth/libcmyth/ringbuf.c -@@ -145,7 +145,7 @@ - - control = rec->rec_conn; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]SETUP_RING_BUFFER[]:[]0", -@@ -237,7 +237,7 @@ - new_rec->rec_ring->ringbuf_fill = fill; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return new_rec; - } -@@ -344,7 +344,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - if(len > rec->rec_ring->conn_data->conn_tcp_rcvbuf) - len = rec->rec_ring->conn_data->conn_tcp_rcvbuf; -@@ -374,7 +374,7 @@ - ret = c; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -410,7 +410,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - return -EINVAL; - } - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock (&rec->rec_conn->conn_mutex); - - if(len > rec->rec_ring->conn_data->conn_tcp_rcvbuf) - len = rec->rec_ring->conn_data->conn_tcp_rcvbuf; -@@ -507,7 +507,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - - ret = (int32_t)(cur - buf); - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock (&rec->rec_conn->conn_mutex); - return ret; - } - -@@ -550,7 +550,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - if ((offset == 0) && (whence == WHENCE_CUR)) - return ring->file_pos; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]SEEK_RINGBUF[]:[]%"PRId32"[]:[]%"PRId32"[]:[]%"PRId8"[]:[]%"PRId32"[]:[]%"PRId32, -@@ -593,7 +593,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - ret = ring->file_pos; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -diff --git a/lib/cmyth/libcmyth/socket.c b/lib/cmyth/libcmyth/socket.c -index 53b461d..fe94bee 100644 ---- a/lib/cmyth/libcmyth/socket.c -+++ b/lib/cmyth/libcmyth/socket.c -@@ -369,6 +369,17 @@ - } - } - -+ if (sep_start && conn->conn_buf[conn->conn_pos] != (unsigned char)*state) { -+ /* -+ * Reset separator in case the current character does not match -+ * the expected part of the separator. This needs to take place -+ * before checking if the current character starts a new separator. -+ * (To resolve issues with strings that look like [[]:[]) -+ */ -+ sep_start = NULL; -+ state = separator; -+ } -+ - if (conn->conn_buf[conn->conn_pos] == (unsigned char)*state) { - /* - * We matched the next (possibly first) step -@@ -378,13 +389,6 @@ - sep_start = &buf[placed]; - } - ++state; -- } else { -- /* -- * No match with separator, reset the state to the -- * beginning. -- */ -- sep_start = NULL; -- state = separator; - } - - if (placed < buflen) { -diff --git a/lib/cmyth/libcmyth/storagegroup.c b/lib/cmyth/libcmyth/storagegroup.c -index 7a2d1f4..9cce74e 100644 ---- a/lib/cmyth/libcmyth/storagegroup.c -+++ b/lib/cmyth/libcmyth/storagegroup.c -@@ -175,7 +175,7 @@ - return 0; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_SG_GETFILELIST[]:[]%s[]:[]%s[]:[][]:[]1", hostname, storagegroup); - -@@ -246,7 +246,7 @@ - cmyth_dbg(CMYTH_DBG_DEBUG, "%s: results= %d\n", __FUNCTION__, res); - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return ret; - } - -@@ -284,7 +284,7 @@ - return 0; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_SG_FILEQUERY[]:[]%s[]:[]%s[]:[]%s", hostname, storagegroup, filename); - -@@ -343,7 +343,7 @@ - cmyth_dbg(CMYTH_DBG_DEBUG, "%s: filename: %s\n", __FUNCTION__, ret->filename); - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return ret; - } - -diff --git a/lib/cmyth/libcmyth/timestamp.c b/lib/cmyth/libcmyth/timestamp.c -index 60dd5f5..5355190 100644 ---- a/lib/cmyth/libcmyth/timestamp.c -+++ b/lib/cmyth/libcmyth/timestamp.c -@@ -60,7 +60,7 @@ - ret->timestamp_hour = 0; - ret->timestamp_minute = 0; - ret->timestamp_second = 0; -- ret->timestamp_isdst = 0; -+ ret->timestamp_isdst = -1; - return ret; - } - -@@ -201,8 +201,7 @@ - cmyth_timestamp_t - cmyth_timestamp_from_tm(struct tm * tm_datetime) - { -- cmyth_timestamp_t ret; -- ret = cmyth_timestamp_create(); -+ cmyth_timestamp_t ret = cmyth_timestamp_create(); - if (!ret) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timestamp\n", - __FUNCTION__); -@@ -263,15 +262,22 @@ - time_t - cmyth_timestamp_to_unixtime(cmyth_timestamp_t ts) - { -- struct tm tm; -- tm.tm_sec = ts->timestamp_second; -- tm.tm_min = ts->timestamp_minute; -- tm.tm_hour = ts->timestamp_hour; -- tm.tm_mday = ts->timestamp_day; -- tm.tm_mon = ts->timestamp_month-1; -- tm.tm_year = ts->timestamp_year-1900; -- tm.tm_isdst = ts->timestamp_isdst; -- return mktime(&tm); -+ struct tm tm_datetime; -+ -+ if (!ts) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timestamp provided\n", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ tm_datetime.tm_sec = ts->timestamp_second; -+ tm_datetime.tm_min = ts->timestamp_minute; -+ tm_datetime.tm_hour = ts->timestamp_hour; -+ tm_datetime.tm_mday = ts->timestamp_day; -+ tm_datetime.tm_mon = ts->timestamp_month-1; -+ tm_datetime.tm_year = ts->timestamp_year-1900; -+ tm_datetime.tm_isdst = ts->timestamp_isdst; -+ return mktime(&tm_datetime); - } - - /* -@@ -285,6 +291,7 @@ - * user supplied buffer 'str'. The size of 'str' must be - * CMYTH_TIMESTAMP_LEN + 1 or this will overwrite beyond 'str'. - * -+ * Format: ISO-8601 '%Y-%m-%dT%H:%i:%s' "2013-03-21T18:02:59" - * - * Return Value: - * -@@ -327,6 +334,7 @@ - * user supplied buffer 'str'. The size of 'str' must be - * CMYTH_TIMESTAMP_LEN + 1 or this will overwrite beyond 'str'. - * -+ * Format: ISO-8601 '%Y-%m-%d' "2013-03-21" - * - * Return Value: - * -@@ -356,8 +364,7 @@ - } - - int --cmyth_timestamp_to_display_string(char *str, cmyth_timestamp_t ts, -- int time_format_12) -+cmyth_timestamp_to_display_string(char *str, cmyth_timestamp_t ts, int time_format_12) - { - if (!str) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL output string provided\n", -@@ -455,7 +462,48 @@ - return 0; - } - -- -+/* -+ * cmyth_timestamp_to_numstring(char *str, cmyth_timestamp_t ts) -+ * -+ * Scope: PUBLIC -+ * -+ * Description -+ * -+ * Create a string from the timestamp structure 'ts' and put it in the -+ * user supplied buffer 'str'. The size of 'str' must be -+ * CMYTH_TIMESTAMP_NUMERIC_LEN + 1 or this will overwrite beyond 'str'. -+ * -+ * Format: Numeric big endian '%Y%m%d%H%i%s' "20130321180259" -+ * -+ * Return Value: -+ * -+ * Success: 0 -+ * -+ * Failure: -(ERRNO) -+ */ -+int -+cmyth_timestamp_to_numstring(char *str, cmyth_timestamp_t ts) -+{ -+ if (!str) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL output string provided\n", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ if (!ts) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timestamp provided\n", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ sprintf(str, -+ "%4.4ld%2.2ld%2.2ld%2.2ld%2.2ld%2.2ld", -+ ts->timestamp_year, -+ ts->timestamp_month, -+ ts->timestamp_day, -+ ts->timestamp_hour, -+ ts->timestamp_minute, -+ ts->timestamp_second); -+ return 0; -+} - - /* - * cmyth_timestamp_compare(cmyth_timestamp_t ts1, cmyth_timestamp_t ts2) --- -1.8.1.6 - From 388840b5c6dd81a36804e7ec5fa1e7fd5969965e Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 08:59:57 +0200 Subject: [PATCH 08/15] xbmc-pvr-addons: update to xbmc-pvr-addons-frodo-78b0aae Signed-off-by: Stephan Raue --- packages/mediacenter/xbmc-pvr-addons/meta | 2 +- .../xbmc-pvr-addons-frodo-PR193.patch | 2039 ----------------- 2 files changed, 1 insertion(+), 2040 deletions(-) delete mode 100644 packages/mediacenter/xbmc-pvr-addons/patches/frodo-d37ec1d/xbmc-pvr-addons-frodo-PR193.patch diff --git a/packages/mediacenter/xbmc-pvr-addons/meta b/packages/mediacenter/xbmc-pvr-addons/meta index 98d13eb4aa..0f684d9b2e 100644 --- a/packages/mediacenter/xbmc-pvr-addons/meta +++ b/packages/mediacenter/xbmc-pvr-addons/meta @@ -19,7 +19,7 @@ ################################################################################ PKG_NAME="xbmc-pvr-addons" -PKG_VERSION="frodo-d37ec1d" +PKG_VERSION="frodo-78b0aae" if [ "$XBMC" = "master" ]; then PKG_VERSION="b84a88f" fi diff --git a/packages/mediacenter/xbmc-pvr-addons/patches/frodo-d37ec1d/xbmc-pvr-addons-frodo-PR193.patch b/packages/mediacenter/xbmc-pvr-addons/patches/frodo-d37ec1d/xbmc-pvr-addons-frodo-PR193.patch deleted file mode 100644 index eba6743f2e..0000000000 --- a/packages/mediacenter/xbmc-pvr-addons/patches/frodo-d37ec1d/xbmc-pvr-addons-frodo-PR193.patch +++ /dev/null @@ -1,2039 +0,0 @@ -From b49a30690e99de085d35c194c95b11bd79a5c76d Mon Sep 17 00:00:00 2001 -From: Christian Fetzer -Date: Wed, 17 Apr 2013 19:17:38 +0200 -Subject: [PATCH] [mythtv-cmyth] Release v1.6.10 - ---- - addons/pvr.mythtv.cmyth/addon/addon.xml.in | 2 +- - addons/pvr.mythtv.cmyth/addon/changelog.txt | 7 ++ - addons/pvr.mythtv.cmyth/src/client.h | 6 ++ - .../src/cppmyth/MythConnection.cpp | 12 +-- - .../pvr.mythtv.cmyth/src/cppmyth/MythConnection.h | 3 +- - .../src/cppmyth/MythEventHandler.cpp | 4 +- - .../src/cppmyth/MythEventHandler.h | 18 ++-- - .../src/cppmyth/MythProgramInfo.cpp | 12 +-- - .../pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.h | 6 +- - .../pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp | 2 +- - .../pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp | 7 ++ - .../pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h | 3 + - addons/pvr.mythtv.cmyth/src/fileOps.cpp | 2 +- - addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp | 41 ++++---- - lib/cmyth/include/cmyth/cmyth.h | 6 +- - lib/cmyth/libcmyth/bookmark.c | 8 +- - lib/cmyth/libcmyth/cmyth_local.h | 11 +-- - lib/cmyth/libcmyth/cmyth_msc.h | 4 +- - lib/cmyth/libcmyth/commbreak.c | 12 +-- - lib/cmyth/libcmyth/connection.c | 108 ++++++--------------- - lib/cmyth/libcmyth/file.c | 54 +++++++---- - lib/cmyth/libcmyth/input.c | 4 +- - lib/cmyth/libcmyth/livetv.c | 22 +++-- - lib/cmyth/libcmyth/proginfo.c | 53 ++++------ - lib/cmyth/libcmyth/proglist.c | 10 +- - lib/cmyth/libcmyth/recorder.c | 52 +++++----- - lib/cmyth/libcmyth/ringbuf.c | 16 +-- - lib/cmyth/libcmyth/socket.c | 18 ++-- - lib/cmyth/libcmyth/storagegroup.c | 8 +- - lib/cmyth/libcmyth/timestamp.c | 78 ++++++++++++--- - 30 files changed, 311 insertions(+), 278 deletions(-) - -diff --git a/addons/pvr.mythtv.cmyth/addon/addon.xml.in b/addons/pvr.mythtv.cmyth/addon/addon.xml.in -index 35841df..1b631eb 100644 ---- a/addons/pvr.mythtv.cmyth/addon/addon.xml.in -+++ b/addons/pvr.mythtv.cmyth/addon/addon.xml.in -@@ -1,7 +1,7 @@ - - - -diff --git a/addons/pvr.mythtv.cmyth/addon/changelog.txt b/addons/pvr.mythtv.cmyth/addon/changelog.txt -index f31b589..fd63ca7 100644 ---- a/addons/pvr.mythtv.cmyth/addon/changelog.txt -+++ b/addons/pvr.mythtv.cmyth/addon/changelog.txt -@@ -1,3 +1,10 @@ -+v1.6.10 -+- Fixed crash when setting up Live TV (in rare cases) -+- Fixed issue with not updated recording list (Daylight Saving Time) -+- Fixed issue with empty recording list (caused by '[' character) -+- Fixed playback of recordings made by a slave backend -+- Removed 'Failed to connect to MythTV Backend' notification -+ - v1.6.9 - - Added Live TV vs. recording conflict handling - - Fixed recovering from backend connection loss on Windows -diff --git a/addons/pvr.mythtv.cmyth/src/client.h b/addons/pvr.mythtv.cmyth/src/client.h -index a577752..5b98095 100644 ---- a/addons/pvr.mythtv.cmyth/src/client.h -+++ b/addons/pvr.mythtv.cmyth/src/client.h -@@ -36,6 +36,12 @@ - #define strdup _strdup // # strdup is POSIX, _strdup should be used instead - #endif - -+#define TCP_RCV_BUF_CONTROL_SIZE 128000 // Inherited from MythTV's MythSocket class -+#define RCV_BUF_CONTROL_SIZE 32000 // Buffer size to parse backend response from control connection -+#define TCP_RCV_BUF_DATA_SIZE 65536 // TCP buffer for video stream (best performance) -+#define RCV_BUF_DATA_SIZE 64 // Buffer size to parse backend response from data control connection -+#define RCV_BUF_IMAGE_SIZE 32000 // Buffer size to download artworks -+ - #define LIVETV_CONFLICT_STRATEGY_HASLATER 0 - #define LIVETV_CONFLICT_STRATEGY_STOPTV 1 - #define LIVETV_CONFLICT_STRATEGY_CANCELREC 2 -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp -index 10bd80c..3607744 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.cpp -@@ -77,7 +77,7 @@ - , m_port(port) - , m_pEventHandler(NULL) - { -- cmyth_conn_t connection = cmyth_conn_connect_ctrl(const_cast(server.c_str()), port, 64 * 1024, 16 * 1024); -+ cmyth_conn_t connection = cmyth_conn_connect_ctrl(const_cast(server.c_str()), port, RCV_BUF_CONTROL_SIZE, TCP_RCV_BUF_CONTROL_SIZE); - *m_conn_t = connection; - } - -@@ -249,13 +249,13 @@ MythProgramInfo MythConnection::GetRecordedProgram(const CStdString &basename) - return retval; - } - --MythProgramInfo MythConnection::GetRecordedProgram(int chanid, time_t recstartts) -+MythProgramInfo MythConnection::GetRecordedProgram(int chanid, const MythTimestamp &recstartts) - { - MythProgramInfo retval; - cmyth_proginfo_t prog = NULL; -- if (chanid > 0 && recstartts > 0) -+ if (chanid > 0 && !recstartts.IsNull()) - { -- CMYTH_CONN_CALL_REF(prog, prog == NULL, cmyth_proginfo_get_from_timeslot(*m_conn_t, chanid, recstartts)); -+ CMYTH_CONN_CALL_REF(prog, prog == NULL, cmyth_proginfo_get_from_timeslot(*m_conn_t, chanid, *recstartts.m_timestamp_t)); - if (prog) { - retval = MythProgramInfo(prog); - } -@@ -334,7 +334,7 @@ MythFile MythConnection::ConnectFile(MythProgramInfo &recording) - // When file is not NULL doesn't need to mean there is no more connection, - // so always check after calling cmyth_conn_connect_file if still connected to control socket. - cmyth_file_t file = NULL; -- CMYTH_CONN_CALL_REF(file, true, cmyth_conn_connect_file(*recording.m_proginfo_t, *m_conn_t, 64 * 1024, 16 * 1024)); -+ CMYTH_CONN_CALL_REF(file, true, cmyth_conn_connect_file(*recording.m_proginfo_t, *m_conn_t, RCV_BUF_DATA_SIZE, TCP_RCV_BUF_DATA_SIZE)); - MythFile retval = MythFile(file, *this); - return retval; - } -@@ -342,7 +342,7 @@ MythFile MythConnection::ConnectFile(MythProgramInfo &recording) - MythFile MythConnection::ConnectPath(const CStdString &filename, const CStdString &storageGroup) - { - cmyth_file_t file = NULL; -- CMYTH_CONN_CALL_REF(file, file == NULL, cmyth_conn_connect_path(const_cast(filename.c_str()), *m_conn_t, 64 * 1024, 64 * 1024, const_cast(storageGroup.c_str()))); -+ CMYTH_CONN_CALL_REF(file, file == NULL, cmyth_conn_connect_path(const_cast(filename.c_str()), *m_conn_t, RCV_BUF_DATA_SIZE, TCP_RCV_BUF_DATA_SIZE, const_cast(storageGroup.c_str()))); - return MythFile(file, *this); - } - -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h -index 1aab54f..f40fc78 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythConnection.h -@@ -35,6 +35,7 @@ - class MythEventHandler; - class MythRecordingRule; - class MythStorageGroupFile; -+class MythTimestamp; - - template class MythPointer; - template class MythPointerThreadSafe; -@@ -72,7 +73,7 @@ class MythConnection - bool DeleteRecording(MythProgramInfo &recording); - ProgramInfoMap GetRecordedPrograms(); - MythProgramInfo GetRecordedProgram(const CStdString &basename); -- MythProgramInfo GetRecordedProgram(int chanid, time_t recstartts); -+ MythProgramInfo GetRecordedProgram(int chanid, const MythTimestamp &recstartts); - - // Timers - ProgramInfoMap GetPendingPrograms(); -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp -index d546748..e790c65 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.cpp -@@ -109,7 +109,7 @@ class MythEventHandler::MythEventHandlerPrivate : public CThread, public CMutex - , m_hang(false) - , m_recordingChangeEventList() - { -- *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), port, 64 * 1024, 16 * 1024); -+ *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), port, RCV_BUF_CONTROL_SIZE, TCP_RCV_BUF_CONTROL_SIZE); - } - - MythEventHandler::MythEventHandlerPrivate::~MythEventHandlerPrivate() -@@ -460,7 +460,7 @@ void MythEventHandler::MythEventHandlerPrivate::RetryConnect() - usleep(999999); - ref_release(*m_conn_t); - *m_conn_t = NULL; -- *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), m_port, 64 * 1024, 16 * 1024); -+ *m_conn_t = cmyth_conn_connect_event(const_cast(m_server.c_str()), m_port, RCV_BUF_CONTROL_SIZE, TCP_RCV_BUF_CONTROL_SIZE); - - if (*m_conn_t == NULL) - XBMC->Log(LOG_NOTICE, "%s - Could not connect client to event socket", __FUNCTION__); -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h -index 7e58ceb..072a4c5 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythEventHandler.h -@@ -75,21 +75,16 @@ class MythEventHandler - class RecordingChangeEvent - { - public: -- RecordingChangeEvent(RecordingChangeType type, unsigned int chanid, char *recstartts) -+ RecordingChangeEvent(RecordingChangeType type, unsigned int chanid, const CStdString &recstartts) - : m_type(type) - , m_channelID(chanid) -- , m_recordStartTimeSlot(0) - { -- if (recstartts) { -- MythTimestamp time(recstartts, false); -- m_recordStartTimeSlot = time.UnixTime(); -- } -+ m_recordingStartTimeSlot = MythTimestamp(recstartts, false); - } - - RecordingChangeEvent(RecordingChangeType type, const MythProgramInfo &prog) - : m_type(type) - , m_channelID(0) -- , m_recordStartTimeSlot(0) - , m_prog(prog) - { - } -@@ -97,20 +92,19 @@ class MythEventHandler - RecordingChangeEvent(RecordingChangeType type) - : m_type(type) - , m_channelID(0) -- , m_recordStartTimeSlot(0) - { - } - - RecordingChangeType Type() const { return m_type; } - unsigned int ChannelID() const { return m_channelID; }; -- time_t RecordingStartTimeslot() const { return m_recordStartTimeSlot; } -+ MythTimestamp RecordingStartTimeslot() const { return m_recordingStartTimeSlot; } - MythProgramInfo Program() const { return m_prog; } - - private: - RecordingChangeType m_type; -- unsigned int m_channelID; // ADD and DELETE -- time_t m_recordStartTimeSlot; // ADD and DELETE -- MythProgramInfo m_prog; // UPDATE -+ unsigned int m_channelID; // ADD and DELETE -+ MythTimestamp m_recordingStartTimeSlot; // ADD and DELETE -+ MythProgramInfo m_prog; // UPDATE - }; - - bool HasRecordingChangeEvent() const; -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.cpp -index 1632c41..352e601 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.cpp -@@ -24,7 +24,7 @@ - - MythProgramInfo::MythProgramInfo() - : m_proginfo_t() -- , m_framerate(-1) -+ , m_frameRate(-1) - , m_coverart("") - , m_fanart("") - { -@@ -32,7 +32,7 @@ - - MythProgramInfo::MythProgramInfo(cmyth_proginfo_t cmyth_proginfo) - : m_proginfo_t(new MythPointer()) -- , m_framerate(-1) -+ , m_frameRate(-1) - , m_coverart("") - , m_fanart("") - { -@@ -208,14 +208,14 @@ int MythProgramInfo::Priority() - return cmyth_proginfo_priority(*m_proginfo_t); // Might want to use recpriority2 instead - } - --void MythProgramInfo::SetFramerate(const long long framerate) -+void MythProgramInfo::SetFrameRate(const long long frameRate) - { -- m_framerate = framerate; -+ m_frameRate = frameRate; - } - --long long MythProgramInfo::Framterate() const -+long long MythProgramInfo::FrameRate() const - { -- return m_framerate; -+ return m_frameRate; - } - - CStdString MythProgramInfo::IconPath() -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.h -index c90cab9..bd8ba73 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythProgramInfo.h -@@ -68,8 +68,8 @@ class MythProgramInfo - time_t RecordingEndTime(); - int Priority(); - -- void SetFramerate(const long long framerate); -- long long Framterate() const; -+ void SetFrameRate(const long long frameRate); -+ long long FrameRate() const; - - CStdString IconPath(); - CStdString Coverart() const; -@@ -79,7 +79,7 @@ class MythProgramInfo - boost::shared_ptr > m_proginfo_t; - - // Cached PVR attributes -- long long m_framerate; -+ long long m_frameRate; - - // Artworks - CStdString m_coverart; -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp -index 0df71af..81cebea 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythRecorder.cpp -@@ -159,7 +159,7 @@ bool MythRecorder::SpawnLiveTV(MythChannel &channel) - // Check channel - *m_liveChainUpdated = 0; - cmyth_recorder_t recorder = NULL; -- recorder = cmyth_spawn_live_tv(*m_recorder_t, 64*1024, 64*1024, MythRecorder::prog_update_callback, &pErr, const_cast(channel.Number().c_str())); -+ recorder = cmyth_spawn_live_tv(*m_recorder_t, RCV_BUF_DATA_SIZE, TCP_RCV_BUF_DATA_SIZE, MythRecorder::prog_update_callback, &pErr, const_cast(channel.Number().c_str())); - - if (recorder && pErr == NULL) { - *m_recorder_t = recorder; -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp -index 8518c63..f8ec968 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.cpp -@@ -94,3 +94,10 @@ CStdString MythTimestamp::DisplayString(bool use12hClock) - bool succeded=cmyth_timestamp_to_display_string(time, *m_timestamp_t, use12hClock) == 0; - return succeded ? CStdString(time) : CStdString(""); - } -+ -+CStdString MythTimestamp::NumString() -+{ -+ char time[15]; -+ bool succeded = cmyth_timestamp_to_numstring(time, *m_timestamp_t) == 0; -+ return succeded ? CStdString(time) : CStdString(""); -+} -\ No newline at end of file -diff --git a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h -index ecff511..a4ee3a5 100644 ---- a/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h -+++ b/addons/pvr.mythtv.cmyth/src/cppmyth/MythTimestamp.h -@@ -32,6 +32,8 @@ - class MythTimestamp - { - public: -+ friend class MythConnection; -+ - MythTimestamp(); - MythTimestamp(cmyth_timestamp_t cmyth_timestamp); - MythTimestamp(CStdString time, bool datetime); -@@ -50,6 +52,7 @@ class MythTimestamp - CStdString String(); - CStdString IsoString(); - CStdString DisplayString(bool use12hClock); -+ CStdString NumString(); - - private: - boost::shared_ptr > m_timestamp_t; -diff --git a/addons/pvr.mythtv.cmyth/src/fileOps.cpp b/addons/pvr.mythtv.cmyth/src/fileOps.cpp -index ec7089b..b51e6d8 100644 ---- a/addons/pvr.mythtv.cmyth/src/fileOps.cpp -+++ b/addons/pvr.mythtv.cmyth/src/fileOps.cpp -@@ -298,7 +298,7 @@ bool FileOps::CacheFile(const CStdString &localFilename, MythFile &source) - unsigned long long totalLength = source.Length(); - unsigned long long totalRead = 0; - -- const long buffersize = 32768; -+ const long buffersize = RCV_BUF_IMAGE_SIZE; - char* buffer = new char[buffersize]; - - while (totalRead < totalLength) -diff --git a/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp b/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp -index 0a86552..73e9a7e 100644 ---- a/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp -+++ b/addons/pvr.mythtv.cmyth/src/pvrclient-mythtv.cpp -@@ -186,7 +186,6 @@ bool PVRClientMythTV::Connect() - if (!m_con.IsConnected()) - { - XBMC->Log(LOG_ERROR,"Failed to connect to MythTV backend on %s:%d", g_szMythHostname.c_str(), g_iMythPort); -- XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30300)); - return false; - } - -@@ -605,7 +604,7 @@ void PVRClientMythTV::EventUpdateRecordings() - } - } - else -- XBMC->Log(LOG_ERROR, "%s - Add recording failed for %u %ld", __FUNCTION__, event.ChannelID(), event.RecordingStartTimeslot()); -+ XBMC->Log(LOG_ERROR, "%s - Add recording failed for %u %s", __FUNCTION__, event.ChannelID(), event.RecordingStartTimeslot().NumString().c_str()); - break; - } - case MythEventHandler::CHANGE_UPDATE: -@@ -618,7 +617,7 @@ void PVRClientMythTV::EventUpdateRecordings() - XBMC->Log(LOG_DEBUG, "%s - Update recording: %s", __FUNCTION__, prog.UID().c_str()); - - // Copy cached framerate -- prog.SetFramerate(it->second.Framterate()); -+ prog.SetFrameRate(it->second.FrameRate()); - - // Fill artwork - m_db.FillRecordingArtwork(prog); -@@ -662,7 +661,7 @@ void PVRClientMythTV::ForceUpdateRecording(ProgramInfoMap::iterator it) - if (!prog.IsNull()) - { - // Copy cached framerate -- prog.SetFramerate(it->second.Framterate()); -+ prog.SetFrameRate(it->second.FrameRate()); - - // Fill artwork - m_db.FillRecordingArtwork(prog); -@@ -776,10 +775,10 @@ PVR_ERROR PVRClientMythTV::SetRecordingLastPlayedPosition(const PVR_RECORDING &r - if (it != m_recordings.end()) - { - // pin framerate value -- if (it->second.Framterate() < 0) -- it->second.SetFramerate(m_db.GetRecordingFrameRate(it->second)); -+ if (it->second.FrameRate() < 0) -+ it->second.SetFrameRate(m_db.GetRecordingFrameRate(it->second)); - // Calculate the frame offset -- long long frameOffset = (long long)(lastplayedposition * it->second.Framterate() / 1000.0f); -+ long long frameOffset = (long long)(lastplayedposition * it->second.FrameRate() / 1000.0f); - if (frameOffset < 0) frameOffset = 0; - if (g_bExtraDebug) - { -@@ -831,9 +830,9 @@ int PVRClientMythTV::GetRecordingLastPlayedPosition(const PVR_RECORDING &recordi - XBMC->Log(LOG_DEBUG, "%s - FrameOffset: %lld)", __FUNCTION__, frameOffset); - } - // Pin framerate value -- if (it->second.Framterate() <0) -- it->second.SetFramerate(m_db.GetRecordingFrameRate(it->second)); -- float frameRate = (float)it->second.Framterate() / 1000.0f; -+ if (it->second.FrameRate() <0) -+ it->second.SetFrameRate(m_db.GetRecordingFrameRate(it->second)); -+ float frameRate = (float)it->second.FrameRate() / 1000.0f; - if (frameRate > 0) - { - bookmark = (int)((float)frameOffset / frameRate); -@@ -1306,7 +1305,10 @@ bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel) - m_pEventHandler->SetRecorder(m_rec); - - if (m_rec.SpawnLiveTV((*channelByNumberIt).second)) -+ { -+ XBMC->Log(LOG_DEBUG, "%s - Done", __FUNCTION__); - return true; -+ } - } - } - } -@@ -1324,19 +1326,14 @@ bool PVRClientMythTV::OpenLiveStream(const PVR_CHANNEL &channel) - - XBMC->Log(LOG_ERROR,"%s - Failed to open live stream", __FUNCTION__); - XBMC->QueueNotification(QUEUE_WARNING, XBMC->GetLocalizedString(30305)); // Channel unavailable -- -- return false; - } - else - { -- if (g_bExtraDebug) -- XBMC->Log(LOG_DEBUG,"%s - Done", __FUNCTION__); -- -- return true; -+ XBMC->Log(LOG_ERROR, "%s - Live stream is already opened. recorder: %lu", __FUNCTION__, m_rec.ID()); - } -+ return false; - } - -- - void PVRClientMythTV::CloseLiveStream() - { - if (g_bExtraDebug) -@@ -1527,7 +1524,14 @@ bool PVRClientMythTV::OpenRecordedStream(const PVR_RECORDING &recording) - // Enable playback mode: Keep quiet on connection - m_pEventHandler->EnablePlayback(); - -- m_file = m_con.ConnectFile(it->second); -+ // Currently we only request the stream from the master backend. -+ // Future implementations could request the stream from slaves if not available on the master. -+ -+ // Create dedicated control connection for file playback; smart pointer deletes it when file gets deleted. -+ MythConnection fileControlConnection(g_szMythHostname, g_iMythPort); -+ if (!fileControlConnection.IsNull()) -+ m_file = fileControlConnection.ConnectFile(it->second); -+ - m_pEventHandler->SetRecordingListener(recording.strRecordingId, m_file); - - // Resume fileOps -@@ -1554,6 +1558,7 @@ void PVRClientMythTV::CloseRecordedStream() - XBMC->Log(LOG_DEBUG, "%s", __FUNCTION__); - - m_file = MythFile(); -+ m_pEventHandler->SetRecordingListener("", m_file); - - m_pEventHandler->DisablePlayback(); - -diff --git a/lib/cmyth/include/cmyth/cmyth.h b/lib/cmyth/include/cmyth/cmyth.h -index 9de2d59..e790b65 100644 ---- a/lib/cmyth/include/cmyth/cmyth.h -+++ b/lib/cmyth/include/cmyth/cmyth.h -@@ -630,7 +630,7 @@ extern cmyth_recorder_t cmyth_spawn_live_tv(cmyth_recorder_t rec, - char ** err, char * channame); - - extern cmyth_recorder_t cmyth_livetv_chain_setup(cmyth_recorder_t old_rec, -- int32_t tcp_rcvbuf, -+ uint32_t buflen, int32_t tcp_rcvbuf, - void (*prog_update_callback)(cmyth_proginfo_t)); - - extern int32_t cmyth_livetv_get_block(cmyth_recorder_t rec, char *buf, -@@ -730,7 +730,7 @@ extern int cmyth_timestamp_to_display_string(char *str, cmyth_timestamp_t ts, - - extern int cmyth_datetime_to_string(char *str, cmyth_timestamp_t ts); - --extern cmyth_timestamp_t cmyth_datetime_from_string(char *str); -+extern int cmyth_timestamp_to_numstring(char *str, cmyth_timestamp_t ts); - - extern int cmyth_timestamp_compare(cmyth_timestamp_t ts1, - cmyth_timestamp_t ts2); -@@ -837,7 +837,7 @@ extern cmyth_proginfo_t cmyth_proginfo_get_from_basename(cmyth_conn_t control, - */ - extern cmyth_proginfo_t cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, - uint32_t chanid, -- time_t recstartts); -+ const cmyth_timestamp_t recstartts); - - /** - * Retrieve the title of a program. -diff --git a/lib/cmyth/libcmyth/bookmark.c b/lib/cmyth/libcmyth/bookmark.c -index dde35f6..12d534d 100644 ---- a/lib/cmyth/libcmyth/bookmark.c -+++ b/lib/cmyth/libcmyth/bookmark.c -@@ -41,7 +41,7 @@ int64_t cmyth_get_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog) - } - sprintf(buf,"%s %"PRIu32" %s","QUERY_BOOKMARK",prog->proginfo_chanId, - start_ts_dt); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex);; - if ((err = cmyth_send_message(conn,buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -66,7 +66,7 @@ int64_t cmyth_get_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog) - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return ret; - } - -@@ -94,7 +94,7 @@ int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, int64_t bookmar - sprintf(buf, "SET_BOOKMARK %"PRIu32" %s %"PRId32" %"PRId32, prog->proginfo_chanId, - start_ts_dt, (int32_t)(bookmark >> 32), (int32_t)(bookmark & 0xffffffff)); - } -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex);; - if ((ret = cmyth_send_message(conn,buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -106,6 +106,6 @@ int cmyth_set_bookmark(cmyth_conn_t conn, cmyth_proginfo_t prog, int64_t bookmar - __FUNCTION__); - } - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return ret; - } -diff --git a/lib/cmyth/libcmyth/cmyth_local.h b/lib/cmyth/libcmyth/cmyth_local.h -index 42d6477..619db48 100644 ---- a/lib/cmyth/libcmyth/cmyth_local.h -+++ b/lib/cmyth/libcmyth/cmyth_local.h -@@ -40,10 +40,6 @@ - - #if defined(_MSC_VER) - #include "cmyth_msc.h" --//#define PTHREAD_MUTEX_INITIALIZER NULL; --#define PTHREAD_MUTEX_INITIALIZER InitializeCriticalSection(&mutex); --//typedef void* pthread_mutex_t; --typedef CRITICAL_SECTION pthread_mutex_t; - #else - #include - #include -@@ -58,9 +54,6 @@ - #define closesocket(fd) close(fd) - #endif /* _MSC_VER */ - --#define mutex __cmyth_mutex --extern pthread_mutex_t mutex; -- - /* - * Some useful constants - */ -@@ -69,6 +62,7 @@ - #define CMYTH_INT16_LEN (sizeof("-65536") - 1) - #define CMYTH_INT8_LEN (sizeof("-256") - 1) - #define CMYTH_TIMESTAMP_LEN (sizeof("YYYY-MM-DDTHH:MM:SS") - 1) -+#define CMYTH_TIMESTAMP_NUMERIC_LEN (sizeof("YYYYMMDDHHMMSS") - 1) - #define CMYTH_DATESTAMP_LEN (sizeof("YYYY-MM-DD") - 1) - #define CMYTH_UTC_LEN (sizeof("1240120680") - 1) - #define CMYTH_COMMBREAK_START 4 -@@ -91,6 +85,7 @@ struct cmyth_conn { - char * server; /**< hostname of server */ - uint16_t port; /**< port of server */ - cmyth_conn_ann_t conn_ann; /**< connection announcement */ -+ pthread_mutex_t conn_mutex; - }; - - /* Sergio: Added to support new livetv protocol */ -@@ -104,6 +99,7 @@ struct cmyth_livetv_chain { - char **chain_urls; - cmyth_file_t *chain_files; /* File pointers for the urls */ - volatile int8_t livetv_watch; /* JLB: Manage program breaks */ -+ int32_t livetv_buflen; - int32_t livetv_tcp_rcvbuf; - int32_t livetv_block_len; - }; -@@ -255,6 +251,7 @@ struct cmyth_proginfo { - struct cmyth_proglist { - cmyth_proginfo_t *proglist_list; - int proglist_count; -+ pthread_mutex_t proglist_mutex; - }; - - /* -diff --git a/lib/cmyth/libcmyth/cmyth_msc.h b/lib/cmyth/libcmyth/cmyth_msc.h -index 76cea08..28050fe 100644 ---- a/lib/cmyth/libcmyth/cmyth_msc.h -+++ b/lib/cmyth/libcmyth/cmyth_msc.h -@@ -36,11 +36,11 @@ - #pragma warning(disable:4267) - #pragma warning(disable:4996) - -+#define pthread_mutex_init(a, b) InitializeCriticalSection(a) -+#define pthread_mutex_destroy(a) DeleteCriticalSection(a) - #define pthread_mutex_lock(a) EnterCriticalSection(a) - #define pthread_mutex_unlock(a) LeaveCriticalSection(a) --#define PTHREAD_MUTEX_INITIALIZER InitializeCriticalSection(&mutex); - typedef CRITICAL_SECTION pthread_mutex_t; --extern pthread_mutex_t mutex; - - #define SHUT_RDWR SD_BOTH - -diff --git a/lib/cmyth/libcmyth/commbreak.c b/lib/cmyth/libcmyth/commbreak.c -index 4bf66b2..2a28265 100644 ---- a/lib/cmyth/libcmyth/commbreak.c -+++ b/lib/cmyth/libcmyth/commbreak.c -@@ -110,7 +110,7 @@ - - sprintf(buf,"%s %"PRIu32" %ld", "QUERY_COMMBREAK", prog->proginfo_chanId, - (long)cmyth_timestamp_to_unixtime(prog->proginfo_rec_start_ts)); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if ((err = cmyth_send_message(conn, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -134,7 +134,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return breaklist; - } - -@@ -156,7 +156,7 @@ - - sprintf(buf,"%s %"PRIu32" %ld", "QUERY_CUTLIST", prog->proginfo_chanId, - (long)cmyth_timestamp_to_unixtime(prog->proginfo_rec_start_ts)); -- -+ pthread_mutex_lock(&conn->conn_mutex); - if ((err = cmyth_send_message(conn, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -180,7 +180,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return breaklist; - } - -@@ -287,7 +287,7 @@ int cmyth_rcv_commbreaklist(cmyth_conn_t conn, int *err, - int r; - - start_ts_dt = cmyth_timestamp_to_unixtime(prog->proginfo_rec_start_ts); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if ((r=cmyth_mysql_get_commbreak_list(db, prog->proginfo_chanId, start_ts_dt, breaklist, conn->conn_version)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_mysql_get_commbreak_list() failed (%d)\n", -@@ -302,6 +302,6 @@ int cmyth_rcv_commbreaklist(cmyth_conn_t conn, int *err, - breaklist->commbreak_count = 0; - } - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return breaklist; - } -diff --git a/lib/cmyth/libcmyth/connection.c b/lib/cmyth/libcmyth/connection.c -index 24a2145..9abe834 100644 ---- a/lib/cmyth/libcmyth/connection.c -+++ b/lib/cmyth/libcmyth/connection.c -@@ -33,31 +33,6 @@ - static char * cmyth_conn_get_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting); - static int cmyth_conn_set_setting_unlocked(cmyth_conn_t conn, const char* hostname, const char* setting, const char* value); - --#ifdef _MSC_VER --CRITICAL_SECTION mutex; -- --BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) --{ -- switch( ul_reason_for_call ) -- { -- case DLL_PROCESS_ATTACH: -- InitializeCriticalSection(&mutex); -- break; -- /*case DLL_THREAD_ATTACH: -- ... -- case DLL_THREAD_DETACH: -- ...*/ -- case DLL_PROCESS_DETACH: -- DeleteCriticalSection(&mutex); -- break; -- } -- return TRUE; --} -- --#else --pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; --#endif -- - typedef struct { - unsigned int version; - char token[14]; // up to 13 chars used in v74 + the terminating NULL character -@@ -106,7 +81,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return; - } - if (!conn->conn_hang && conn->conn_ann != ANN_NONE) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - /* - * Try to shut down the connection. Can't do much -@@ -118,7 +93,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - __FUNCTION__, err); - } - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - } - } - -@@ -146,6 +121,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - { - free( conn->server ); - } -+ pthread_mutex_destroy(&conn->conn_mutex); - cmyth_dbg(CMYTH_DBG_DEBUG, "%s }\n", __FUNCTION__); - } - -@@ -186,6 +162,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - ret->server = NULL; - ret->port = 0; - ret->conn_ann = ANN_NONE; -+ pthread_mutex_init(&ret->conn_mutex, NULL); - cmyth_dbg(CMYTH_DBG_DEBUG, "%s }\n", __FUNCTION__); - return ret; - } -@@ -258,13 +235,12 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - - temp = tcp_rcvbuf; - size = sizeof(temp); -+ cmyth_dbg(CMYTH_DBG_DEBUG, "%s: setting socket option SO_RCVBUF to %d", __FUNCTION__, tcp_rcvbuf); - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, size); - if(getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, &size)) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: could not get rcvbuf from socket(%d)\n", - __FUNCTION__, errno); -- temp = tcp_rcvbuf; - } -- tcp_rcvbuf = temp; - - if (getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf), NI_NUMERICHOST)) { - strcpy(namebuf, "[unknown]"); -@@ -376,13 +352,12 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - - temp = conn->conn_tcp_rcvbuf; - size = sizeof(temp); -+ cmyth_dbg(CMYTH_DBG_DEBUG, "%s: setting socket option SO_RCVBUF to %d", __FUNCTION__, conn->conn_tcp_rcvbuf); - setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, size); - if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&temp, &size)) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: could not get rcvbuf from socket(%d)\n", - __FUNCTION__, errno); -- temp = conn->conn_tcp_rcvbuf; - } -- conn->conn_tcp_rcvbuf = temp; - - if (getnameinfo(addr->ai_addr, addr->ai_addrlen, namebuf, sizeof(namebuf), portbuf, sizeof(portbuf), NI_NUMERICHOST)) { - strcpy(namebuf, "[unknown]"); -@@ -881,7 +856,6 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - { - cmyth_conn_t conn = NULL; - char *announcement = NULL; -- char *myth_host = NULL; - char reply[16]; - int err = 0; - int count = 0; -@@ -905,33 +879,11 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - __FUNCTION__); - goto shut; - } -- cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", -- __FUNCTION__); -- if (control->conn_version >= 17) { -- myth_host = cmyth_conn_get_setting(control, prog->proginfo_host, -- "BackendServerIP"); -- if (myth_host && (strcmp(myth_host, "-1") == 0)) { -- ref_release(myth_host); -- myth_host = NULL; -- } -- } -- if (!myth_host) { -- cmyth_dbg(CMYTH_DBG_PROTO, -- "%s: BackendServerIP setting not found. Using proginfo_host: %s\n", -- __FUNCTION__, prog->proginfo_host); -- myth_host = ref_alloc(strlen(prog->proginfo_host) + 1); -- strcpy(myth_host, prog->proginfo_host); -- } -- conn = cmyth_connect(myth_host, prog->proginfo_port, -- buflen, tcp_rcvbuf); -- cmyth_dbg(CMYTH_DBG_PROTO, -- "%s: done connecting data connection, conn = %d\n", -- __FUNCTION__, conn); -+ cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", __FUNCTION__); -+ conn = cmyth_connect(control->server, control->port, buflen, tcp_rcvbuf); -+ cmyth_dbg(CMYTH_DBG_PROTO, "%s: done connecting data connection, conn = %d\n", __FUNCTION__, conn); - if (!conn) { -- cmyth_dbg(CMYTH_DBG_ERROR, -- "%s: cmyth_connect(%s, %"PRIu16", %"PRIu32") failed\n", -- __FUNCTION__, -- myth_host, prog->proginfo_port, buflen); -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %"PRIu16", %"PRIu32") failed\n", __FUNCTION__, control->server, control->port, buflen); - goto shut; - } - /* -@@ -1016,12 +968,10 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - ret->file_data = conn; - ret->file_id = file_id; - ret->file_length = file_length; -- ref_release(myth_host); - return ret; - - shut: - ref_release(conn); -- ref_release(myth_host); - return NULL; - } - -@@ -1390,7 +1340,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return NULL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((rec=cmyth_recorder_create()) == NULL) - goto fail; -@@ -1436,7 +1386,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - conn->conn_tcp_rcvbuf) < 0) - goto fail; - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return rec; - -@@ -1444,7 +1394,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - if (rec) - ref_release(rec); - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return NULL; - } -@@ -1483,7 +1433,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return NULL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((rec=cmyth_recorder_create()) == NULL) - goto fail; -@@ -1534,7 +1484,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - conn->conn_tcp_rcvbuf) < 0) - goto fail; - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return rec; - -@@ -1542,7 +1492,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - if (rec) - ref_release(rec); - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return NULL; - } -@@ -1563,7 +1513,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - if ((total == NULL) || (used == NULL)) - return -EINVAL; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if (control->conn_version >= 32) - { snprintf(msg, sizeof(msg), "QUERY_FREE_SPACE_SUMMARY"); } -@@ -1633,7 +1583,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - - return ret; - } -@@ -1675,7 +1625,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return -1; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - snprintf(msg, sizeof(msg), "GET_FREE_RECORDER_COUNT"); - if ((err = cmyth_send_message(conn, msg)) < 0) { -@@ -1704,7 +1654,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - ret = (int)c; - - err: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return ret; - } -@@ -1715,7 +1665,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - int count, err; - char* result = NULL; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if(conn->conn_version < 17) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support QUERY_HOSTNAME\n", - __FUNCTION__); -@@ -1758,7 +1708,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - buffer[sizeof(buffer)-1] = 0; - cmyth_dbg(CMYTH_DBG_ERROR, "%s: odd left over data %s\n", __FUNCTION__, buffer); - } -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - if(!strcmp("-1",result)) { - cmyth_dbg(CMYTH_DBG_PROTO, "%s: Failed to retrieve backend hostname.\n", -@@ -1768,7 +1718,7 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - return result; - - err: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - if(result) - ref_release(result); - -@@ -1853,9 +1803,9 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserv - { - char* result = NULL; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - result = cmyth_conn_get_setting_unlocked(conn, hostname, setting); -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return result; - } -@@ -1900,9 +1850,9 @@ int cmyth_conn_set_setting(cmyth_conn_t conn, - { - int result; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - result = cmyth_conn_set_setting_unlocked(conn, hostname, setting, value); -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return result; - } -@@ -1970,7 +1920,7 @@ int cmyth_conn_set_setting(cmyth_conn_t conn, - } - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((err = cmyth_send_message(conn, msg)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -1987,6 +1937,6 @@ int cmyth_conn_set_setting(cmyth_conn_t conn, - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return err; - } -\ No newline at end of file -diff --git a/lib/cmyth/libcmyth/file.c b/lib/cmyth/libcmyth/file.c -index 2b2feb8..5aeaf02 100644 ---- a/lib/cmyth/libcmyth/file.c -+++ b/lib/cmyth/libcmyth/file.c -@@ -51,7 +51,7 @@ - return; - } - if (file->file_control) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - /* - * Try to shut down the file transfer. Can't do much -@@ -74,7 +74,7 @@ - goto fail; - } - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - ref_release(file->file_control); - } - if (file->closed_callback) { -@@ -409,7 +409,7 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - if(len > file->file_data->conn_tcp_rcvbuf) - len = file->file_data->conn_tcp_rcvbuf; -@@ -445,7 +445,7 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - ret = c; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - - return ret; - } -@@ -474,7 +474,7 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - int64_t - cmyth_file_seek(cmyth_file_t file, int64_t offset, int8_t whence) - { -- char msg[128]; -+ char msg[4096]; - int err; - int count; - int64_t c; -@@ -484,13 +484,26 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - if (file == NULL) - return -EINVAL; - -- if ((offset == 0) && (whence == WHENCE_CUR)) -- return file->file_pos; -- -- if ((offset == file->file_pos) && (whence == WHENCE_SET)) -- return file->file_pos; -+ if (whence == WHENCE_CUR) { -+ if (offset == 0) -+ return file->file_pos; -+ ret = file->file_pos + offset; -+ if (ret < 0 || ret > file->file_length) -+ goto inv; -+ } -+ if (whence == WHENCE_SET) { -+ if (offset == file->file_pos) -+ return file->file_pos; -+ if (offset < 0 || offset > file->file_length) -+ goto inv; -+ } -+ if (whence == WHENCE_END) { -+ ret = file->file_length - offset; -+ if (ret < 0 || ret > file->file_length) -+ goto inv; -+ } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - ret = 0; - while(file->file_pos < file->file_req) { -@@ -558,9 +571,14 @@ void cmyth_file_set_closed_callback(cmyth_file_t file, void (*callback)(cmyth_fi - ret = file->file_pos; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - - return ret; -+ -+inv: -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: seek out of range: file: %"PRIu32" pos: %"PRId64" length: %"PRId64" whence: %"PRId8" offset: %"PRId64, -+ __FUNCTION__, file->file_id, file->file_pos, file->file_length, whence, offset); -+ return -1; - } - - /* -@@ -600,7 +618,7 @@ int32_t cmyth_file_read(cmyth_file_t file, char *buf, int32_t len) - if(len > file->file_data->conn_tcp_rcvbuf) - len = file->file_data->conn_tcp_rcvbuf; - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock (&file->file_control->conn_mutex); - - /* make sure we have outstanding requests that fill the buffer that was called with */ - /* this way we should be able to saturate the network connection better */ -@@ -745,7 +763,7 @@ int32_t cmyth_file_read(cmyth_file_t file, char *buf, int32_t len) - - ret = (int32_t)(cur - buf); - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock (&file->file_control->conn_mutex); - return ret; - } - -@@ -780,7 +798,7 @@ int cmyth_file_is_open(cmyth_file_t file) - return -EINVAL; - } - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - snprintf (msg, sizeof (msg), - "QUERY_FILETRANSFER %"PRIu32"[]:[]IS_OPEN", -@@ -812,7 +830,7 @@ int cmyth_file_is_open(cmyth_file_t file) - if (ret == 0) - cmyth_dbg(CMYTH_DBG_ERROR, "%s: file transfer socket is closed\n", __FUNCTION__); - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - return ret; - } - -@@ -845,7 +863,7 @@ int cmyth_file_is_open(cmyth_file_t file) - return -EINVAL; - } - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock(&file->file_control->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_FILETRANSFER %"PRIu32"[]:[]SET_TIMEOUT[]:[]%"PRId32, -@@ -866,7 +884,7 @@ int cmyth_file_is_open(cmyth_file_t file) - ret = 0; - - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock(&file->file_control->conn_mutex); - return ret; - } - -diff --git a/lib/cmyth/libcmyth/input.c b/lib/cmyth/libcmyth/input.c -index 0ecc05a..64f0ed5 100644 ---- a/lib/cmyth/libcmyth/input.c -+++ b/lib/cmyth/libcmyth/input.c -@@ -114,7 +114,7 @@ - } - - sprintf(buf,"QUERY_RECORDER %"PRIu32"[]:[]GET_FREE_INPUTS", rec->rec_id); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - if ((err = cmyth_send_message(rec->rec_conn, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_send_message() failed (%d)\n", -@@ -138,7 +138,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - return inputlist; - } - -diff --git a/lib/cmyth/libcmyth/livetv.c b/lib/cmyth/libcmyth/livetv.c -index 02c303e..b458838 100644 ---- a/lib/cmyth/libcmyth/livetv.c -+++ b/lib/cmyth/libcmyth/livetv.c -@@ -121,6 +121,7 @@ static int cmyth_livetv_chain_add(cmyth_recorder_t rec, char * url, - ret->chain_files = NULL; - ret->progs = NULL; - ret->livetv_watch = 0; /* JLB: Manage program breaks */ -+ ret->livetv_buflen = 0; - ret->livetv_tcp_rcvbuf = 0; - ret->livetv_block_len = 0; - ref_set_destroy(ret, (ref_destroy_t)cmyth_livetv_chain_destroy); -@@ -443,7 +444,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - */ - - if (cmyth_livetv_chain_has_url(rec, url) == -1) { -- ft = cmyth_conn_connect_file(loc_prog, rec->rec_conn, 4096, rec->rec_livetv_chain->livetv_tcp_rcvbuf); -+ ft = cmyth_conn_connect_file(loc_prog, rec->rec_conn, rec->rec_livetv_chain->livetv_buflen, rec->rec_livetv_chain->livetv_tcp_rcvbuf); - if (!ft) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_conn_connect_file(%s) failed\n", -@@ -663,7 +664,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - * caller needs to do this on a failure. - */ - cmyth_recorder_t --cmyth_livetv_chain_setup(cmyth_recorder_t rec, int32_t tcp_rcvbuf, -+cmyth_livetv_chain_setup(cmyth_recorder_t rec, uint32_t buflen, int32_t tcp_rcvbuf, - void (*prog_update_callback)(cmyth_proginfo_t)) - { - -@@ -703,6 +704,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - } - - /* JLB: Set tcp receive buffer for the chain files */ -+ new_rec->rec_livetv_chain->livetv_buflen = buflen; - new_rec->rec_livetv_chain->livetv_tcp_rcvbuf = tcp_rcvbuf; - /* JLB: Manage program breaks. Switch OFF watch signal */ - new_rec->rec_livetv_chain->livetv_watch = 0; -@@ -713,7 +715,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - loc_prog->proginfo_pathname); - - if(cmyth_livetv_chain_has_url(new_rec, url) == -1) { -- ft = cmyth_conn_connect_file(loc_prog, new_rec->rec_conn, 4096, new_rec->rec_livetv_chain->livetv_tcp_rcvbuf); -+ ft = cmyth_conn_connect_file(loc_prog, new_rec->rec_conn, new_rec->rec_livetv_chain->livetv_buflen, new_rec->rec_livetv_chain->livetv_tcp_rcvbuf); - if (!ft) { - cmyth_dbg(CMYTH_DBG_ERROR, - "%s: cmyth_conn_connect_file(%s) failed\n", -@@ -736,7 +738,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - } - else { - /* now switch to the valid program */ -- cmyth_livetv_chain_switch(new_rec, 0); -+ cmyth_livetv_chain_switch_last(new_rec); - new_rec->rec_livetv_chain->chain_switch_on_create = 0; - } - } -@@ -820,7 +822,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - if (dir == 0) - return 1; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - ret = 0; - -@@ -841,9 +843,9 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - "%s: wait until livetv_watch is OFF\n", - __FUNCTION__); - for (i = 0; i < 4; i++) { -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - usleep(500000); -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - if (rec->rec_livetv_chain->livetv_watch == 0) - break; - } -@@ -873,7 +875,7 @@ int cmyth_livetv_chain_has_url(cmyth_recorder_t rec, char * url) - - out: - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1131,7 +1133,7 @@ int32_t cmyth_livetv_chain_read(cmyth_recorder_t rec, char *buf, int32_t len) - whence = WHENCE_SET; - } - -- if (fp && cur >=0) -+ if (cur >=0 && cur < rec->rec_livetv_chain->chain_ct && fp) - { - if ((ret = cmyth_file_seek(fp, offset, whence)) >= 0) { - cur -= rec->rec_livetv_chain->chain_current; -@@ -1302,7 +1304,7 @@ int32_t cmyth_livetv_read(cmyth_recorder_t rec, char *buf, int32_t len) - break; - } - -- if ((rtrn = cmyth_livetv_chain_setup(rec, tcp_rcvbuf, -+ if ((rtrn = cmyth_livetv_chain_setup(rec, buflen, tcp_rcvbuf, - prog_update_callback)) == NULL) { - *err = "Failed to setup livetv."; - goto err; -diff --git a/lib/cmyth/libcmyth/proginfo.c b/lib/cmyth/libcmyth/proginfo.c -index ed7534b..33340cb 100644 ---- a/lib/cmyth/libcmyth/proginfo.c -+++ b/lib/cmyth/libcmyth/proginfo.c -@@ -581,7 +581,7 @@ - sprintf(buf, "DELETE_RECORDING 0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if ((err = cmyth_send_message(control, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -601,7 +601,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -663,7 +663,7 @@ - sprintf(buf, "FORGET_RECORDING 0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if ((err = cmyth_send_message(control, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -683,7 +683,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -745,7 +745,7 @@ - sprintf(buf, "STOP_RECORDING 0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - if ((err = cmyth_send_message(control, buf)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -765,7 +765,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -1477,7 +1477,7 @@ - sprintf(buf, "FILL_PROGRAM_INFO cmyth[]:[]0[]:[]%s", proginfo); - free(proginfo); - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - length = prog->proginfo_Length; - -@@ -1518,7 +1518,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - free(buf); - - return ret; -@@ -1730,7 +1730,7 @@ - cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); - return -1; - } -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - if ((err = cmyth_send_message(conn, msg)) < 0) { - fprintf (stderr, "ERROR %d \n",err); - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -1740,7 +1740,7 @@ - count = cmyth_rcv_length(conn); - cmyth_rcv_proglist(conn, &err, prog, count); - prog_count=cmyth_proglist_get_count(prog); -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - return prog_count; - } - -@@ -1766,7 +1766,7 @@ - * to enumerating all recordings - */ - if(control->conn_version >= 32 && strchr(basename, ' ') == NULL) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDING BASENAME %s", - basename); -@@ -1807,10 +1807,10 @@ - goto out; - } - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return prog; - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - if(prog) - ref_release(prog); - return NULL; -@@ -1846,14 +1846,13 @@ - } - - cmyth_proginfo_t --cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, uint32_t chanid, time_t recstartts) -+cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, uint32_t chanid, const cmyth_timestamp_t recstartts) - { - int err = 0; - int count, i; - char msg[4096]; - cmyth_proginfo_t prog = NULL; - cmyth_proglist_t list = NULL; -- cmyth_timestamp_t ts; - char time[15]; - - if (!control) { -@@ -1862,24 +1861,14 @@ - return NULL; - } - -- ts = cmyth_timestamp_from_unixtime(recstartts); -- if (!ts) { -- cmyth_dbg(CMYTH_DBG_ERROR, "%s: timestamp NULL\n", -- __FUNCTION__); -+ if ((err = cmyth_timestamp_to_numstring(time, recstartts)) < 0) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_timestamp_to_numstring() failed (%d)\n", -+ __FUNCTION__, err); - return NULL; - } - -- sprintf(time, -- "%4.4ld%2.2ld%2.2ld%2.2ld%2.2ld%2.2ld", -- ts->timestamp_year, -- ts->timestamp_month, -- ts->timestamp_day, -- ts->timestamp_hour, -- ts->timestamp_minute, -- ts->timestamp_second); -- - if(control->conn_version >= 32) { -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDING TIMESLOT %"PRIu32" %s", - chanid, time); -@@ -1920,10 +1909,10 @@ - goto out; - } - -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return prog; - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - if(prog) - ref_release(prog); - return NULL; -@@ -1944,7 +1933,7 @@ - __FUNCTION__); - continue; - } -- if (cmyth_timestamp_compare(prog->proginfo_rec_start_ts, ts) != 0 || -+ if (cmyth_timestamp_compare(prog->proginfo_rec_start_ts, recstartts) != 0 || - prog->proginfo_chanId != chanid) { - ref_release(prog); - prog = NULL; -diff --git a/lib/cmyth/libcmyth/proglist.c b/lib/cmyth/libcmyth/proglist.c -index 96777df..63addb5 100644 ---- a/lib/cmyth/libcmyth/proglist.c -+++ b/lib/cmyth/libcmyth/proglist.c -@@ -62,6 +62,7 @@ - if (pl->proglist_list) { - free(pl->proglist_list); - } -+ pthread_mutex_destroy(&pl->proglist_mutex); - } - - /* -@@ -93,6 +94,7 @@ - - ret->proglist_list = NULL; - ret->proglist_count = 0; -+ pthread_mutex_init(&ret->proglist_mutex, NULL); - return ret; - } - -@@ -154,7 +156,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&pl->proglist_mutex); - - for (i=0; iproglist_count; i++) { - if (cmyth_proginfo_compare(prog, pl->proglist_list[i]) == 0) { -@@ -170,7 +172,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&pl->proglist_mutex); - - return ret; - } -@@ -237,7 +239,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&conn->conn_mutex); - - if ((err = cmyth_send_message(conn, msg)) < 0) { - cmyth_dbg(CMYTH_DBG_ERROR, -@@ -282,7 +284,7 @@ - ret = 0; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&conn->conn_mutex); - - return ret; - } -diff --git a/lib/cmyth/libcmyth/recorder.c b/lib/cmyth/libcmyth/recorder.c -index 6029ba7..1d78556 100644 ---- a/lib/cmyth/libcmyth/recorder.c -+++ b/lib/cmyth/libcmyth/recorder.c -@@ -180,7 +180,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]IS_RECORDING", - rec->rec_id); -@@ -205,7 +205,7 @@ - ret = c; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -242,7 +242,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]GET_FRAMERATE", - rec->rec_id); -@@ -269,7 +269,7 @@ - ret = 0; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -458,7 +458,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]CANCEL_NEXT_RECORDING[]:[]%"PRIu32 ,rec->rec_id, cancel == 1); - -@@ -475,7 +475,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -511,7 +511,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - sprintf(Buffer, "QUERY_RECORDER %"PRIu32"[]:[]PAUSE", rec->rec_id); - if ((ret=cmyth_send_message(rec->rec_conn, Buffer)) < 0) { -@@ -530,7 +530,7 @@ - ret = 0; - - err: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -624,7 +624,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]CHANGE_CHANNEL[]:[]%d", -@@ -658,7 +658,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -697,7 +697,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]SET_CHANNEL[]:[]%s", -@@ -731,7 +731,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -889,7 +889,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]CHECK_CHANNEL[]:[]%s", -@@ -910,7 +910,7 @@ - ret = 1; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -976,7 +976,7 @@ - __FUNCTION__); - goto out; - } -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - if(rec->rec_conn->conn_version >= 26) - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]GET_CURRENT_RECORDING", -@@ -1010,7 +1010,7 @@ - } - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return proginfo; - } -@@ -1106,7 +1106,7 @@ - - control = rec->rec_conn; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - t = time(NULL); - tm = localtime(&t); -@@ -1192,7 +1192,7 @@ - ret = 0; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1351,7 +1351,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]SPAWN_LIVETV", - rec->rec_id); -@@ -1373,7 +1373,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1395,7 +1395,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - - /* Get our own IP address */ -@@ -1436,7 +1436,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1454,7 +1454,7 @@ - return -ENOSYS; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]STOP_LIVETV", - rec->rec_id); -@@ -1476,7 +1476,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -1497,7 +1497,7 @@ - if(rec->rec_conn->conn_version >= 26) - return 0; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]DONE_RINGBUF", - rec->rec_id); -@@ -1519,7 +1519,7 @@ - ret = 0; - - fail: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -diff --git a/lib/cmyth/libcmyth/ringbuf.c b/lib/cmyth/libcmyth/ringbuf.c -index 173c5a7..6686a54 100644 ---- a/lib/cmyth/libcmyth/ringbuf.c -+++ b/lib/cmyth/libcmyth/ringbuf.c -@@ -145,7 +145,7 @@ - - control = rec->rec_conn; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]SETUP_RING_BUFFER[]:[]0", -@@ -237,7 +237,7 @@ - new_rec->rec_ring->ringbuf_fill = fill; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return new_rec; - } -@@ -344,7 +344,7 @@ - return -EINVAL; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - if(len > rec->rec_ring->conn_data->conn_tcp_rcvbuf) - len = rec->rec_ring->conn_data->conn_tcp_rcvbuf; -@@ -374,7 +374,7 @@ - ret = c; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -@@ -410,7 +410,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - return -EINVAL; - } - -- pthread_mutex_lock (&mutex); -+ pthread_mutex_lock (&rec->rec_conn->conn_mutex); - - if(len > rec->rec_ring->conn_data->conn_tcp_rcvbuf) - len = rec->rec_ring->conn_data->conn_tcp_rcvbuf; -@@ -507,7 +507,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - - ret = (int32_t)(cur - buf); - out: -- pthread_mutex_unlock (&mutex); -+ pthread_mutex_unlock (&rec->rec_conn->conn_mutex); - return ret; - } - -@@ -550,7 +550,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - if ((offset == 0) && (whence == WHENCE_CUR)) - return ring->file_pos; - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&rec->rec_conn->conn_mutex); - - snprintf(msg, sizeof(msg), - "QUERY_RECORDER %"PRIu32"[]:[]SEEK_RINGBUF[]:[]%"PRId32"[]:[]%"PRId32"[]:[]%"PRId8"[]:[]%"PRId32"[]:[]%"PRId32, -@@ -593,7 +593,7 @@ int32_t cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, int32_t len) - ret = ring->file_pos; - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&rec->rec_conn->conn_mutex); - - return ret; - } -diff --git a/lib/cmyth/libcmyth/socket.c b/lib/cmyth/libcmyth/socket.c -index 53b461d..fe94bee 100644 ---- a/lib/cmyth/libcmyth/socket.c -+++ b/lib/cmyth/libcmyth/socket.c -@@ -369,6 +369,17 @@ - } - } - -+ if (sep_start && conn->conn_buf[conn->conn_pos] != (unsigned char)*state) { -+ /* -+ * Reset separator in case the current character does not match -+ * the expected part of the separator. This needs to take place -+ * before checking if the current character starts a new separator. -+ * (To resolve issues with strings that look like [[]:[]) -+ */ -+ sep_start = NULL; -+ state = separator; -+ } -+ - if (conn->conn_buf[conn->conn_pos] == (unsigned char)*state) { - /* - * We matched the next (possibly first) step -@@ -378,13 +389,6 @@ - sep_start = &buf[placed]; - } - ++state; -- } else { -- /* -- * No match with separator, reset the state to the -- * beginning. -- */ -- sep_start = NULL; -- state = separator; - } - - if (placed < buflen) { -diff --git a/lib/cmyth/libcmyth/storagegroup.c b/lib/cmyth/libcmyth/storagegroup.c -index 7a2d1f4..9cce74e 100644 ---- a/lib/cmyth/libcmyth/storagegroup.c -+++ b/lib/cmyth/libcmyth/storagegroup.c -@@ -175,7 +175,7 @@ - return 0; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_SG_GETFILELIST[]:[]%s[]:[]%s[]:[][]:[]1", hostname, storagegroup); - -@@ -246,7 +246,7 @@ - cmyth_dbg(CMYTH_DBG_DEBUG, "%s: results= %d\n", __FUNCTION__, res); - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return ret; - } - -@@ -284,7 +284,7 @@ - return 0; - } - -- pthread_mutex_lock(&mutex); -+ pthread_mutex_lock(&control->conn_mutex); - - snprintf(msg, sizeof(msg), "QUERY_SG_FILEQUERY[]:[]%s[]:[]%s[]:[]%s", hostname, storagegroup, filename); - -@@ -343,7 +343,7 @@ - cmyth_dbg(CMYTH_DBG_DEBUG, "%s: filename: %s\n", __FUNCTION__, ret->filename); - - out: -- pthread_mutex_unlock(&mutex); -+ pthread_mutex_unlock(&control->conn_mutex); - return ret; - } - -diff --git a/lib/cmyth/libcmyth/timestamp.c b/lib/cmyth/libcmyth/timestamp.c -index 60dd5f5..5355190 100644 ---- a/lib/cmyth/libcmyth/timestamp.c -+++ b/lib/cmyth/libcmyth/timestamp.c -@@ -60,7 +60,7 @@ - ret->timestamp_hour = 0; - ret->timestamp_minute = 0; - ret->timestamp_second = 0; -- ret->timestamp_isdst = 0; -+ ret->timestamp_isdst = -1; - return ret; - } - -@@ -201,8 +201,7 @@ - cmyth_timestamp_t - cmyth_timestamp_from_tm(struct tm * tm_datetime) - { -- cmyth_timestamp_t ret; -- ret = cmyth_timestamp_create(); -+ cmyth_timestamp_t ret = cmyth_timestamp_create(); - if (!ret) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timestamp\n", - __FUNCTION__); -@@ -263,15 +262,22 @@ - time_t - cmyth_timestamp_to_unixtime(cmyth_timestamp_t ts) - { -- struct tm tm; -- tm.tm_sec = ts->timestamp_second; -- tm.tm_min = ts->timestamp_minute; -- tm.tm_hour = ts->timestamp_hour; -- tm.tm_mday = ts->timestamp_day; -- tm.tm_mon = ts->timestamp_month-1; -- tm.tm_year = ts->timestamp_year-1900; -- tm.tm_isdst = ts->timestamp_isdst; -- return mktime(&tm); -+ struct tm tm_datetime; -+ -+ if (!ts) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timestamp provided\n", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ tm_datetime.tm_sec = ts->timestamp_second; -+ tm_datetime.tm_min = ts->timestamp_minute; -+ tm_datetime.tm_hour = ts->timestamp_hour; -+ tm_datetime.tm_mday = ts->timestamp_day; -+ tm_datetime.tm_mon = ts->timestamp_month-1; -+ tm_datetime.tm_year = ts->timestamp_year-1900; -+ tm_datetime.tm_isdst = ts->timestamp_isdst; -+ return mktime(&tm_datetime); - } - - /* -@@ -285,6 +291,7 @@ - * user supplied buffer 'str'. The size of 'str' must be - * CMYTH_TIMESTAMP_LEN + 1 or this will overwrite beyond 'str'. - * -+ * Format: ISO-8601 '%Y-%m-%dT%H:%i:%s' "2013-03-21T18:02:59" - * - * Return Value: - * -@@ -327,6 +334,7 @@ - * user supplied buffer 'str'. The size of 'str' must be - * CMYTH_TIMESTAMP_LEN + 1 or this will overwrite beyond 'str'. - * -+ * Format: ISO-8601 '%Y-%m-%d' "2013-03-21" - * - * Return Value: - * -@@ -356,8 +364,7 @@ - } - - int --cmyth_timestamp_to_display_string(char *str, cmyth_timestamp_t ts, -- int time_format_12) -+cmyth_timestamp_to_display_string(char *str, cmyth_timestamp_t ts, int time_format_12) - { - if (!str) { - cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL output string provided\n", -@@ -455,7 +462,48 @@ - return 0; - } - -- -+/* -+ * cmyth_timestamp_to_numstring(char *str, cmyth_timestamp_t ts) -+ * -+ * Scope: PUBLIC -+ * -+ * Description -+ * -+ * Create a string from the timestamp structure 'ts' and put it in the -+ * user supplied buffer 'str'. The size of 'str' must be -+ * CMYTH_TIMESTAMP_NUMERIC_LEN + 1 or this will overwrite beyond 'str'. -+ * -+ * Format: Numeric big endian '%Y%m%d%H%i%s' "20130321180259" -+ * -+ * Return Value: -+ * -+ * Success: 0 -+ * -+ * Failure: -(ERRNO) -+ */ -+int -+cmyth_timestamp_to_numstring(char *str, cmyth_timestamp_t ts) -+{ -+ if (!str) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL output string provided\n", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ if (!ts) { -+ cmyth_dbg(CMYTH_DBG_ERROR, "%s: NULL timestamp provided\n", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ sprintf(str, -+ "%4.4ld%2.2ld%2.2ld%2.2ld%2.2ld%2.2ld", -+ ts->timestamp_year, -+ ts->timestamp_month, -+ ts->timestamp_day, -+ ts->timestamp_hour, -+ ts->timestamp_minute, -+ ts->timestamp_second); -+ return 0; -+} - - /* - * cmyth_timestamp_compare(cmyth_timestamp_t ts1, cmyth_timestamp_t ts2) --- -1.8.1.6 - From 567cd50215c43121da765efa527146f61bb547ab Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 09:00:23 +0200 Subject: [PATCH 09/15] xbmc-pvr-addons: add PR200 Signed-off-by: Stephan Raue --- .../xbmc-pvr-addons-frodo-PR200.patch | 7312 +++++++++++++++++ 1 file changed, 7312 insertions(+) create mode 100644 packages/mediacenter/xbmc-pvr-addons/patches/frodo-78b0aae/xbmc-pvr-addons-frodo-PR200.patch diff --git a/packages/mediacenter/xbmc-pvr-addons/patches/frodo-78b0aae/xbmc-pvr-addons-frodo-PR200.patch b/packages/mediacenter/xbmc-pvr-addons/patches/frodo-78b0aae/xbmc-pvr-addons-frodo-PR200.patch new file mode 100644 index 0000000000..41fbc236e8 --- /dev/null +++ b/packages/mediacenter/xbmc-pvr-addons/patches/frodo-78b0aae/xbmc-pvr-addons-frodo-PR200.patch @@ -0,0 +1,7312 @@ +From 628a7239b70c157c86eecff15a25243dda7ab0ba Mon Sep 17 00:00:00 2001 +From: Anton Fedchin +Date: Sat, 8 Jun 2013 16:45:02 +0400 +Subject: [PATCH] [iptvsimple] new pvr.iptvsimple addon. + +--- + README | 2 + + addons/Makefile.am | 8 +- + addons/pvr.iptvsimple/Makefile.am | 19 + + addons/pvr.iptvsimple/addon/addon.xml.in | 27 + + addons/pvr.iptvsimple/addon/icon.png | Bin 0 -> 63858 bytes + addons/pvr.iptvsimple/addon/iptv.m3u | 0 + .../addon/resources/language/English/strings.po | 72 + + .../addon/resources/language/French/strings.po | 71 + + .../addon/resources/language/Russian/strings.po | 70 + + addons/pvr.iptvsimple/addon/resources/settings.xml | 18 + + .../project/VS2010Express/pvr.iptvsimple.vcxproj | 100 + + .../VS2010Express/pvr.iptvsimple.vcxproj.filters | 33 + + addons/pvr.iptvsimple/src/PVRIptvData.cpp | 977 ++++++++ + addons/pvr.iptvsimple/src/PVRIptvData.h | 122 + + addons/pvr.iptvsimple/src/client.cpp | 438 ++++ + addons/pvr.iptvsimple/src/client.h | 54 + + configure.ac | 13 +- + lib/rapidxml/license.txt | 52 + + lib/rapidxml/manual.html | 406 +++ + lib/rapidxml/rapidxml.hpp | 2596 ++++++++++++++++++++ + lib/rapidxml/rapidxml_iterators.hpp | 175 ++ + lib/rapidxml/rapidxml_print.hpp | 421 ++++ + lib/rapidxml/rapidxml_utils.hpp | 122 + + project/BuildDependencies/scripts/zlib_d.bat | 13 + + project/BuildDependencies/scripts/zlib_d.txt | 2 + + project/VS2010Express/xbmc-pvr-addons.sln | 6 + + 26 files changed, 5814 insertions(+), 3 deletions(-) + create mode 100644 addons/pvr.iptvsimple/Makefile.am + create mode 100644 addons/pvr.iptvsimple/addon/addon.xml.in + create mode 100644 addons/pvr.iptvsimple/addon/icon.png + create mode 100644 addons/pvr.iptvsimple/addon/iptv.m3u + create mode 100644 addons/pvr.iptvsimple/addon/resources/language/English/strings.po + create mode 100644 addons/pvr.iptvsimple/addon/resources/language/French/strings.po + create mode 100644 addons/pvr.iptvsimple/addon/resources/language/Russian/strings.po + create mode 100644 addons/pvr.iptvsimple/addon/resources/settings.xml + create mode 100644 addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj + create mode 100644 addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj.filters + create mode 100644 addons/pvr.iptvsimple/src/PVRIptvData.cpp + create mode 100644 addons/pvr.iptvsimple/src/PVRIptvData.h + create mode 100644 addons/pvr.iptvsimple/src/client.cpp + create mode 100644 addons/pvr.iptvsimple/src/client.h + create mode 100644 lib/rapidxml/license.txt + create mode 100644 lib/rapidxml/manual.html + create mode 100644 lib/rapidxml/rapidxml.hpp + create mode 100644 lib/rapidxml/rapidxml_iterators.hpp + create mode 100644 lib/rapidxml/rapidxml_print.hpp + create mode 100644 lib/rapidxml/rapidxml_utils.hpp + create mode 100644 project/BuildDependencies/scripts/zlib_d.bat + create mode 100644 project/BuildDependencies/scripts/zlib_d.txt + +diff --git a/README b/README +index 7694add..e530e28 100644 +--- a/README ++++ b/README +@@ -30,6 +30,8 @@ List of addons with dependencies: + - MythTV addon: + Build dependencies: libmysqlclient, boost (headers) + Runtime dependencies: libmysqlclient ++- IPTV Simple addon: ++ Build dependencies: zlib + + ============================= + Windows +diff --git a/addons/Makefile.am b/addons/Makefile.am +index 00987b4..c677998 100644 +--- a/addons/Makefile.am ++++ b/addons/Makefile.am +@@ -1,5 +1,8 @@ + if ADDON_MYTHTV +- ADDITIONAL_SUBDIRS = pvr.mythtv.cmyth ++ ADDON_MYTHTV_SUBDIRS = pvr.mythtv.cmyth ++endif ++if ADDON_IPTVSIMPLE ++ ADDON_IPTVSIMPLE_SUBDIRS = pvr.iptvsimple + endif + + SUBDIRS = pvr.demo \ +@@ -11,7 +14,8 @@ SUBDIRS = pvr.demo \ + pvr.njoy \ + pvr.vuplus \ + pvr.argustv \ +- $(ADDITIONAL_SUBDIRS) ++ $(ADDON_MYTHTV_SUBDIRS) \ ++ $(ADDON_IPTVSIMPLE_SUBDIRS) + + clean: + -rm -f *.zip +diff --git a/addons/pvr.iptvsimple/Makefile.am b/addons/pvr.iptvsimple/Makefile.am +new file mode 100644 +index 0000000..a82a2f4 +--- /dev/null ++++ b/addons/pvr.iptvsimple/Makefile.am +@@ -0,0 +1,19 @@ ++# ++# Makefile for the PVR IPTV Simple add-on for XBMC PVR ++# ++# See the README for copyright information and ++# how to reach the author. ++# ++ ++ADDONBINNAME = XBMC_IPTV_Simple ++ADDONNAME = pvr.iptvsimple ++LIBNAME = libpvriptvsimple-addon ++lib_LTLIBRARIES = libpvriptvsimple-addon.la ++ ++include ../Makefile.include.am ++ ++libpvriptvsimple_addon_la_SOURCES = src/client.cpp \ ++ src/PVRIptvData.cpp ++libpvriptvsimple_addon_la_LDFLAGS = $(ZLIB_LIBS) @TARGET_LDFLAGS@ ++ ++ +diff --git a/addons/pvr.iptvsimple/addon/addon.xml.in b/addons/pvr.iptvsimple/addon/addon.xml.in +new file mode 100644 +index 0000000..185c3ab +--- /dev/null ++++ b/addons/pvr.iptvsimple/addon/addon.xml.in +@@ -0,0 +1,27 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ XBMC PVR addon for IPTV support. https://github.com/afedchin/xbmc-addon-iptvsimple/wiki/IPTV-Simple-Home ++ XBMC PVR аддон для поддержки IPTV. https://github.com/afedchin/xbmc-addon-iptvsimple/wiki/IPTV-Simple-Home ++ IPTV Simple PVR Client support m3u playlists, streaming of Live TV for multicast/unicast sources, listening to Radio channels and EPG. ++ Интерфейс для IPTV. Поддерживает просмотр потокового ТВ для юникаст/мультикаст источников, прослушивание радиоканалов и работу с электронным телегидом. ++ This is unstable software! The authors are in no way responsible for failed playings, incorrect EPG times, wasted hours, or any other undesirable effects. ++ Это нестабильная программа! Авторы не несут ответственности за неудачные воспроизведение, неверные время EPG, потраченное время и прочие нежелательные эффекты. ++ @OS@ ++ ++ +diff --git a/addons/pvr.iptvsimple/addon/icon.png b/addons/pvr.iptvsimple/addon/icon.png +new file mode 100644 +index 0000000000000000000000000000000000000000..c3a8e4924133562db5eb32ab4ca94b677af6a27c +GIT binary patch +literal 63858 +zcmbSSRYM$2(_JL!;_en)g1b8ecMa|i!EJF!aJS&@1b26LXR+Y!&c}1{{(~c}&6)4y()z{ygN8Drz6jQrM9^%0Z;q<5-1wZhm@Q};j(pFo|4I_yb_e%jAd)QV +z*B~`&RD4Q%uJRG|m&B5bBQYrDTm(!SeA-{uC1u_h555-F9AJ_niewtb41JZ-e%RW6G;LvHUHV1R!jh&1|O`H|A*mR +zjw|BOUrp$K^v4SQgDQ_|;{_gGH&k&Ttqq*xOl02 +zbA{gfp9*HyYr}<`yp}m6ie=HIuo<8{Jc|mf>05Mh&Jx(+Ogoymb#`$icqLfr@c^Mp +zYzZ_>`kC!F`%7`L%T1`W>(ljtWa(w~E=_>M#~?~r*Bna7GcoGFfL=h+>>Aw4yZKud +znQwIVkL)96b7<8G!|Kx{L|RpH&R&w+7tt=1=NHirR{WrFIU}71uw{!S%wt@QUF{@h)9I+*B{d6lCsb)(#~Igy#2c1`=dir=oKS*C;)2ldv#VFS=i=h3)&eg#qXVA(+}clC-^PV2vBcqpB_{Czu)pP><)IVFl~Jl@9rllRA%`i* +z$FXQB6IFzv3lfDRln?>T)<&dpz+kvS%v;}HW@cx5kD-PxspI3mt-Fxi9o{VVy$Cp;OK#R{O!IJRoBfQ=W@KUUazlO2 +zcNOA&(Q#0h{c&1J@)Bk6GDQ-C`X23vZ5e44P}|7Nfps}2^ubx>x@wV=7?@etTa=gl +zt7>Xh)AJGb$GvZJs;C75TIP4$OC+He?4R02(BnCYO7w_iw5&@yhT{F>9up5;9&}g5 +zvzu^!McEvHlPSMVF7Rb)2JypqUs%j~y@Sn%iHodFB3u%l4B7;4)NL0F3Epp&Sc9>C +zg6%|=H+}m14AQ^oWbn#Jh-8>){5DIeP(dn^3Zj{PdE^$Nv?@bNbk3tWUxD4Y;Zv|^ +z&G>bthA_^PhRI%Zh*w34v5B$3tEidbWY+G&}&A-+v+(mbSOthjg-v~za-6pne~ +zTfHR<E^(_w}o|n71eYj}5Tb +zlEW(xNEOfsae6$2+p9(o@*5)XzprHY*~@EyfPqRwYmW#S*b)fQ{%rP5MMqPEEGw!efyRp#0KcVj6%=(DV3r|>{!ZA<3yS&-XK;D;)9Hcl$!BW6 +zt*`jJ8#42xP@_f@3qb#cH-mcdN*p94l2q5sK%aqMCRWk0(+sADP4ZzM`nB5-k}fR>^Q +z{l30ij!i+>0+xNwxqQdu6}<Rtnfs|Ms?7cYoz)25@*xGXSLFk78|IBD1MbwI3IL +zZ|szLLlzl;t+`|Le^|?Y?57qwGlNAY^axxT$jCVH=o3=+^CpaI0xghH~I}cqdxN@F<1X$lwV74)D#2fhxKjp=>K@E +zpG2=hGFSD;s;UT-c;ySJ?D1ak4qmu1H<6|H)8!bNyVB;-%nbiG=Dr>h< +z{b9o$oxGOqyyf5=<>Tt(=0)h?r0Y5R1FpLRHv?`ET5`3UVDl9lre8d=2o-R5!t8g6 +z<=;p3pHbsLp+UoyAO4UQ&2Os^%y)MxRq9ao-YLa@9a{dr3HIN%`Q|^3HO2LEJiIOD +z(Ys6h`=r|Y&}xkYz(CjUl(4m43k2jlDdwz1HDIGk|Ix>5KoD+$6jwi$U?+hI@4rWg +zE;6FA4x)0bJoKuw3lSqAO#MRDz-nHH+`t;z!%nF}0G@Bf&kVpkH4>T8p>NF`@TgF? +za|@`$jl6DNAVZ!oWnvBArEjTpKo$#%TBit$i%ght4FsLqv8ylyE#J{nC+6}_qN3%v +zm2u0IqcTA0XG3sun}X=ec(>t#E6&ouUzk4tEgM3>2@W1Yx%X+2=^Dp@>;L-lp0^GEly6#kx3?43!(a11=%v5$^UzgVn1%Cg?TF +zUd<1tNc+1`y5Id@->TnXV_LUas-^WeQ_!~$xBEOs6S6=y)t%0s%z(ynkI3&3bZpJP +z!r0wch@$#(5)L+P?!93WVdnQ4v3PL+XB!H(gT +z{v%UJckLwX+%v!Kdfv>Q2VEwZk=PUd5j3-jGoP)M-P<#wIx{~E<>%}QCuX6Hm_%ekmI@3vbZ%w +zBkLg}>SQS`6hA#)!Z4Ym^4l7lKQMR2rI#jy+^)lMhf4XCk#KNaiy{4B6%`j0cBfOq +zK|5zm-HA)d?6C|eljT{$Mpp_w&U(`!FhcpQzo($Yugx7To*;`U+MZ|7a?1gm%&bC& +z&ury^R?=WagvlK)-GYg#)&9hzJ0BRxC0Gih6ZeyU@HI*~DxX6{Ri#;d&Qb0}l@K*b +zBN!n(7K8RxCOCSMQTWgzxD(@Rw#roY+rY%5|3{ZlPej5RO~cM4U+3$K!&6K4v!=o0 +z>p>^T99yg3aOA7U?o?+;n6Xa0rE<036SxNBlG^_nhmZv?lEmjpc86=E3&s$m``tq5 +z;)|l!`HjJ)N!`=xzwt;Z{SP>wx4BMao12!U4nR!@A`mL51u$>iU8trMC~L~SliHu3 +zDE!sQ6DF3gHjP}g?YShEIgaxb+Bm!yKdjM_)TSN0Wipl4sxwN;18dF8eQm*l6Al*v +zy)AbZ7W!2lgN4cv9K~HR^;<%DM?vV+LLTN-8(mOqks!Sn)bIwPA)UvcNGs!3rDJ2 +zlVKh-BPAFMb5R9I*ds0WCLICuO^8c(&sl>$Gz-x(ygfNw*@21_pVWpGE&Aonm7cKo +zt(eQ&450`Wdbh@=u19Z@$Frr57ov}UC}gYM@Zk=I_^dzWyED%K+ZGBN+3x_c>q0l{cE0%ICULIfgqQz?AS?8Q_#U;|1>oomOAN^(GsWLc +z&DF3+D8e82j6D>6??ff7DZO)#CKEASC|9XY>4g71l41BOJRV{Ayqd!dO=N0Yl}mv> +zFo;5#9Fji2blrF@5q(riZQv}>&E&pC##E%)2se=%-I~_=p?F3sa%%wFf$lH!$i7%? +zbU6t`6sS<&GB8f|89@n7tgyeQKd>*awWDf8$d)EIGD@9pDVf^PBuPssKmQcJQ09SO +zrj?bP+5L~DlyQW;RDZU9r6SG-|KkeRmWsW{t-u!< +zU{(W)Nv6OPxR3=mJP}TL?z3HP*;EaBgFn1Ux2_NPKfXBddR$J&biE0+-*L^c79!bQQZMUIzCc<`v|!3+fmKB=Os@k67)a? +zKRvuRV)#Qif&o7NIw989+InUvg(=Z3Fi`7`r5AosfGX&2Ksx29*y)KF#d@wg*n3L; +z)n)O-4)Vc?tj|n52~cV#N=SJ?^X#TbwvsI41het(%z(ndi2NcfEkChKc{mK)$nIC5 +zOhTZ_qzGcHAL2Dq^j-ri9j;!8Hk{Z@qdqJ32a|b7NlQb`>KTwp9`OpgaH~L<0sFcW +zU4>Snu+>T_Y6eo4*-gGEo*n{gCLOp)OUW#b;Ut+fo(*Caf`nbX*Keap`=0;J{by@| +zCR??r(bz9)71;0v2PGw&8>1Rl|FrC#M?)Z%sQ0O(fD`+g5 +zL~PRYzCqDv^yJ6uUrk>wP@gQljVxdO?LCRlz;EeiH~U-9M&}t|j%9KZx)3wfJ8{6H +z!Tsn`=dE%~%Mph-t-#)@&E-SanXcnyw;v$y-2b2Wz+~wxH+^~s5@f3a|Ef$!PM?zA +z7Y-0qAcIvRQZZTLYWQ-hFkY}~omV~OfiRE1832oK4Utk@y=NiYa8>~~s$|Qwv^bK* +zgp@lVSta)G>O$WZ`r6QUW6ALQ-2Eipli;*&bu;fF_TU{n=e&62-%4H9P|1emZVYV+ +zl~!uKKU=GK`t~}x_am8s3WIPHGHI+lPz%Mr!R;jPX0&{XAeSb4mY__wUu6#a`~**5Lm`#b6Ra6`!b&7!txW}E +z?=%;w;C5%uRoD_#F5{SWhNr@{;B;`KE%9f&%vI`H#Mvqmf2rs~o8!{KjljcN1`h$7 +zss;8^*@0o4Q|H$vy2U+oo*5egz=bp|JQ&wivyi0uSyw#$r6)c?6#T$KXyEUjiwunx +zEr$AUtQSQqg$3wBs1eU~hhOwlt^yAP5xIW&6X8bw +zhaOYf4?kH5GLV=opN;Hyr#ONiH={TSmh3p1OsnwW!YH`k-+9!o{fFHB9-0J?tfn4u +z3=4+VJp{6D#*1IZ9!??q?5|`H-Nj%@?OPj9Iv^0od5{52+%C1&IP`hY|QAv)rEx8fgx+rk(mKMXFsfAF)a} +zZ>Ib(Af+@4sn5k`BQZFjPWX$Xe``U^p|Nwq?Dqv?E(12-;Y%8G!R%L!C3i+0$dZ36 +z&vt^y7QI!`hsHE#2Ip*{5)zd`yjOoS#P^j5Le2uK#e4|n>xo&VvO@&RAKg`uf#aEf +zqEuwD4(RD9j=0s)oEfK%*(Tz!mP|Lq?|c9^Nm^^s?xvnN>IpmXd@LsOl#|6Qzu!*w +zR33dv9#P+ScC!INCnSD{5AS#WUN7I?c|m++Xpm#XkMwl7XVfw6pln5+jY{nn_Al0B3# +zQx4D_JP-dDSxlsOHQEM7r9*K4di<37THje~!Yti8l?YsS$a^AdWSt#7pM<1!KjiII`pOHy#`&=XKaK!Zp(9y&iWnl7ff*%N +zU%t+OAJ_-#yf8|0S?pwijLRaA7`LJtp+Kj&ctQRI$ecLE)01vJ#<)t~dyKWr<3!@Oa}?Cw(hI-Q}YH_E--QXdyKANNN3q(zc#q7t~!0s +zLNbMqnXXx@pDa|C&6$cp?w3}JMBc(O5&jsdqalV|8F;$b-zx20I +z89z1_)lq9%;j_12^Je2Oc>6XssZ$}er%WItWuNy@BVn*BO?uF@6?w`X8Et3n4t^X9 +zexzJ)kT<`RnYY%1AK|`?S0+C;q5!XJ-?D794`n7u9)@{wuhH} +z07YM60fZmm9}l}9L$$jpLavLh9@|2XZQM2fh{bd@9UJ_Eo$Ehmllf9?<2(*y!a|&} +z{Y6kRxodx~*}vHMJob?c4hg+G`E1{04;=_SXlHVJqCc$PB%uW|_sj_p$t1PlLIx|< +zq9m>r3W3>U>u_T5g1WyVX-R@{OXl%!_jT^Gomq#Dq;ob6{`O}&k-$&T9!=%m4fr`A +zGc=6h0XWVig!?N6FT770%Xg8RV@(XtTyRwn#eOevrFv+o@?l0;QnnXj)TI5s)b`?> +zrvPTu_A4dTd(Z7Uo;55Pn&b~nGS#%zm2V4?IU81JMlQn4O9Gf*y7eEB?em%3a0DalgKlu2oF{R| +zX?N*-2M4`GX}b;H7K*PrA3AX^yusd=BZ@xg#cpoQ4A`mFl@Y!f=<&RPhWGA}N_O$K +zcriR)bjPFKIQ+A9lch@Z_<1lO0xG)e6Id;fA9gj1OY6=vm9< +z!JU9+W9#vOHAB}CsQH!;<#Vd8m1gbR)-Xcf?<69ND^&ha6H +z1=5qOS8}qq$}0J2rHC~Mq;WZG(q$3&NNP!@XF9AgSUn{OR)wWHGzn8k-Uy|IBIIo#*^-;Su91f*a{QJw?H5s=Gi#D%JZ_Mc)1}lPvv0t(e(AIr37|(t-kh9JkoQ>W$n`ZQF#pQeTE)K$}vXhSZ6c70SrH;5#))uz$UW^(XPa$Kf)uusG6BvSbrBhq-4n6{#Z|CkNG?78DD7hzm1&MJg +zk|}JhPlWnHV&J>OR^-IGnQO=t!A6Yf^QX`X~_{B=x#`&t!BMV6O3WIbf +zH(ri=LSDpJe8Ra_jr%9y9Ye<$FZ*mR%-UYX#m?=`mM>Mu5K?fy`^Zb}IKcAS);49W +zP4f15ZA(XK6lBC=_?dLI%RM!1zw?Mc2AH?9+Ac&>FtT3S54N*k=RUBOezF~M+z9;%fPMfRCe)_q7dr06|2Klf_->8Db*%2~Oq;1Ti +zm#iWRc9({Jz4m{kY(J8(9@=mR|L&8D$vg2X5I;0`sc<;$dw!-`TwI*4o``GZVU0*h +zVPrx~(^CMHNqGikI6LrN&uk{kBj0P&#zicG1WN#%FIzFckdW?f->s$-QfL +z>3!t|{E+n^RuA%?Sp@cnh<*Pj$L>dF1Ce*;0PN26ht0meb$BEO9R1yux>_cvmV$8H +z3Fdwc*S-E#@=U1F&@inZ*)eG$aNc^N@Ol{YO5c9+=b|@2Ov-U3s0sj;T5V@&VuO8-<*JU +z)G)2HU~Jml)EfOqifY4$i8?odTSBMcH6UNMaA|uRuUKfnFz3xq421zAtd4nL|{B@ZF+YG*83t1ed~f;qgLFJ^4|-)+D7?cd4KA9HTvY2q1X80uT1EP +zA)31>;-%3+$=DO|EjkKk#=e|=CJyby{_nNAmrMS>x?Z!Y*;g@}ADs`c@PpoP?0jDQ +z`3GuD<}9%+fS4Dde^v$`%eyHCexGg9F-A6kytg!DASdIEVmm8$MZ}6>!q$C#Vgk?p +z?Y7e6AH6|njPI#Aq5pJJdDEb}`8GG0)2I;+>Cyt|xn!a+}~ +z_?u7B?mORvu4LMu4*ZEV4SccA+Z?zm5U`33@L*zy`z=#|x`V#P1l%=}9@k3($X)jt +zT_I4g$eXwG7CyU6uRURc9w=Y&4cC6@J1Sg0v4Uu*<(?la8t%=#EP-rtLy7pz~zaxDFM7oXO_qjzIuUBVOw0jc%KryLkBk&@GJ~io4Y6G^6 +zeJOCBjvg$Rr#LS<12R@Fy +za-&HzlK}>(g+jySe_DXN5t&4E^uJ7SvN?$?y1ISa@2undC(S3(-?l#wnEtEXl->Ka +zuE>_I{LA5WT->&{e +z-+z&LM~i9S&TU$MWm)4hgpDG80;<;j)f+Y@?bUiSfU3D5$hup{VRl(ulvKQRyhI^^}KzTsr^{Jy5Ft|96=!!LslI58NA)Yf0{-gJrsZKu=k!= +zfiz~*h1p*u(nOH9Nn`d%EtBRTW}v+SAo{`bt-XFEXi60`h(!cP#=qPZn2B`U +z7LP-~y{f)vG4(VW)ybr`t!C_4|Kqm%mavfbN@4)X#!ZgeQfVNWWvO@{h}g +zRS?|V+-mgAo-X(IVZv#A`|7e@AnQX!-B^>Sww@Aii~wlzdC#9T_D#AC$tyd+V?3W2 +zp1gNb;jZV-S7>!Qk5bY!W^IXX-N9~id&uwB_3?4Yq33f~0#tf?`i2~D|H2xXfCW)a +z)%EaT)F|I|V6=gGh-zQ=5w&Ld@m$k2P-j5fM{Sd|S~B25mAlq?%lV7-dY5j06E1;O +zju*cBFtH7WA}PDO##Yhnmb;ca@Mb0bCel)BW4Vx8eTdLz|1VCa0qSl7%gqazZ;@*Y +zV7%3vpZsK`uS|X3Tl?v~JOSU3h|*M#tMnEbgCsxHOc6 +ze13TLo&T_3mt)?U7x(|D6?@X1KOID{@l)0}5Cs1r%FP(s^biP#)BNE(F-OY7bz(_v +zAwlIh8M!6O0Z`>J2P}4-k0^SpEj^^Lu-SX;xh_{Zrv^IJ8{yVyG3d>%wK_AmOBi@-1YZ!sg2v=QFi`R0t${MHBl{t`OSTj*bjr3V?O0V8_{ +zUzenFZRB*n+<@kuMC6I;Z&E4sfWD=(uxjk9f!4ce_Vj0h(1>EZ?1HXm>2X^|=yc`y +z%+YM5`r&>#jY#(m=#Y?p14lqOCGIJ#J2($?uA=hSj_shXTuH +z;_JKnms;@YC`NRRz5npaZvhbbnExyUTXR(+C3G(X-CIi$=JYrXk;%W%j3=sv(j8A_ +z*kxBSb=flIhwR|9d*XC?sl1n1+t?B!CdJhEJP6Z`6;vi?hO1P@K;?WCMZ<-Eb02{i +z@9~rtHxAz$5Hu>Jp%w%r5F+W++@}&>KF(@4xkHyM^E&yi`{D2cjHINNZR4I +zwjdDG{Xp&$#m-S5{A^>8iFl&#e07Iucel3lJ?03&AYF$yhG*@ICuU$IIY5-asQ4>3 +z#`C<}!1iaM?p>0pRW)C+a0%e;_+;;rkAo160CP$HCqvQtZcv56VFI^0E9-jdAE{jq +zEt8EafgysnNlQM-m#Pk4pP=kP(AL&IFJsE-^7C*z2haYL85yN($a5BvfCz)8$zT8t +zc$$zzc0o;FaFQc0RgjuCM78*m2=Iy6&GMcR)w>r8w1tLwFf2|m3#nPcR&Jo>i?mGz +zN4yCd8_|ErpWL9~>Lqabyi@J!d$sl;%(eFNFMC+c#)Nat +zwF0xsugZG#QY9op;0qzE{+2oVe)th``w+hQghu&hG@oR>d^+vZ!8Ef7uo?E@5W!o5 +zNn{0#FR!eKe7U>~`|+FUp?RmQoCKByV&9Ug+tUW(-~_6s%Z;kH;^O7*^WQ3f4ybD1 +zaXv-9{G(${D_lu26o$lJ)t)}#^}3S%!L#{P^{{n#H5Mzp%lDK!%Px`gitHV3e$s<~n0yl(ADwiX=QmbQZ;Mq`<6PEwHtpW)a+g`dyd6&3XX0V?(B6G!jOfE6;5BMZ}A<9+jWOntxC(WcWv_-`!yr` +zv5A5$I&EgkoKzSu;LK6JvvPToY4>_8`sjLRR%pLyNa*!J=r&*~FAbm!mE#nnnQ}7% +z72bcr_o3%>w}4_HJ>nORw7jwo)9L+$Um`u7}gz!CI$2EJVk23Kv#tWw)S56R2v1rTY@_1CmwUNE8@ +zJu|+EF}^(KbG4e8&pp(aP*mKSo-)?N2s#GM3RmW3`3P%uyt`?BpF3Xj*ZRQ4^ND3!{5C1|Zu7`?mXFFGIeqhn82h>~917NbGi}Eito_ +z9s_>6K$-*Iw#B5XzktS~i3Um`Yt_{%Yu|Qk6J(}ts_hOo8nnY6-WI(b!6SC#&pgj7 +zzzkj3*tQ>pY+}kI4#D14t4L8@p=0KXOyG>g&awt`D3wRj*FT$cBpdI=Y(C6>Su~c6 +z76f#Pe*7EX%MD%3eYR&(tU3;YZE)1|P6WkxzKtE&7KoJF&!V$i-A29ALs{=*qsSMp +zo0xoHo8RD>-|(CZv_w6;FM8JqUi?g$cQcIa$foLqFdsB_#}WI&B;*DJI4W6A$$?%r +z;G1QkQY#(o3BQ0$q+)V{eWw^}VR=*6UCF*^5mJl8xAE~kcEkGPd@cbb{;4_)dwS*t +z$nCsf-y8V~`2pCb$Hm%SBi~>$DB}R@n-Ts2EZ1ps6PRFHV@^o;XiCjAG5=F{&5v6) +z^$Dpvu0cV2cNO=`>x{CFQ;sIs9nv6^sFjyfC)c3Lh0$95lfj>v^!wc(^4q@W*~_6Q +zLfsn>dqj1)p;w&uLc~GaF}pL)ACfn}vCeAjTQo9ABH^?$9E|OJNdSf}Cji06wyxg; +z=T#2qmfV}q8mD%XKS{~Y)+bJ-*T)Wu=OK%PIT1o{wW^0dO*(r> +z-2m5|*M$ElfzDR`ac_l&VI6wfUD}_p_jVsDB<#~KmDK$uD+V+Gc4?nrdlL!fn +zwuu`pXyYmuoZgYqo$m^@;|)jGDP+4A&Qao6Vmbb*Cb56{0(}nCUEASrZw3^-67F}! +zI6YdKy925w6r(v4qRe>6O%7B22|t0;sLY^|GX#>xZv=c+Z)amPMoZr(2>nE236HaO +z2HidRn#S(#x;;akra!_vM=Z#DYf!123_q|_@dz?9!fFX2xW6H7iTVI2U}9CXmUL?U +zZL)12rDG@1eR6}^ZOd~b^U)}6?Qb79V&1tvIg_Y~x3E|~26Lba@%cazOg@EoVma&C?OdND& +zTnCf-F&FGXle6fZtfHfeMwm$2xCv$3C9T0k2Twku^EBmd(oq^73Rj}%Cw%AgSeG!> +z;>N2_JxQ@g54aVZT5*~4wg4ZB>;SpX7)J(Y1_GUhzR>tn?bmtokSpV#?lW1M>44eY +zNdP5HI|BsnX~!68JqDnQ^QI=whf>NrqDpNU*CLW{?PmxDj0TJ2y;Vqor?3uW$4GyJ +zi2Zr&r0~X+Q1>T$m{O?o1-Lfy67aL+v!aUX$fQeTmXcz!42t5N)(D4C&#O}v(q9j! +zY9E1lw0yp-jeYah^WbY@k(90$W_hlv}a+pYI&2$nucer%P5hPN6=h7ftRgZHdLhXol(nOi8_ol^&ff0 +za6@PQT~N9*sHO_KI9fM!h#s$sf +ztsovSyan8FL7Y}|txlQO>CiuG$D9$3d^Eu_o@c)hC(%1wBa)cc@yKd#{chSQx>6K9 +z=G}J2=ZG!rOpb%m9MGc+a-8%gQ#ujElQOf`21Q2PW{(P&_91r?imVXB2lqsxt|o*v +zD_pLo$=EGKUPdwoFzZPL+S0M;d2yMVGl!Q~IYoFQvbP*J;IUAiydt2AHmS}mr^OYt +zQK_4`CHb4RnVjvi^# +zo)B?x@5N^uL0BNqJx&bSYxN8Lw&_L?IuH#f(XA*C$!7gGNXvSzDB`DLz^0Ei)l>2P +z2PGGTq?qIuw|digyqJIjhm@q<$;X>5_wcVSg$58Wz#aUC^|Lg^B#T#j7e%)_Xd!%f +z5$en1Z2CJY1BulJpokB$GB8oC6sbU~r(RTyX~_I3|H=dTF6v}au-r~IfLW3cvVkGn +z(VZc*2t4Tig*p#^%}btQIZc+|IY)aPoHpIOFHD)BT*Wk|(3Rtjr#!UAzQCq1r{FXE +zw6@n`HY(t&zimOxvR(+0yWL#|n}aCh>cmSa(94v2;=S&=w6QhYIAT+JB`>XwhJ^2h +z%ebSHu3q-RlY+Q`+Am8UUw6*v(6ivr@g?s~B~97g-PN5m!Z&MmQ)H58k3AKrc3qQQ +zZJHr#PW;`@(S}R&8V<>!?BKYL!p^+aKkA8EXZL1wPI6|Alcj>)6-+&@2JQXU8;7LnLQL447f9y=1!2@Ne7}X8)z@juju&(&xc&n* +z%+qQF=mt(+2aVDg`FHWI8a1mj4-G!QrB$?ITSXxRR!Tl| +zwXVGY$rNQ9c(@D0OzD37!5UH3=xAjU2i(xR7MUa3U2TZ!!Y8{(kHlwS_4nK`6EnbLb*o^w1o{piQ(RK+RCOtWKc{oZNH`}lQ +zoMyrTRIhU_^KYA~~VmgSdplqcxLaVzYEuK|HPT?G)4gT=1s5T}h@ +zBZIa5M9R%(=e2jBz7OQd>9#8^{YQD+9N#~k^f>LenQ!{J>ez_0h44MUX+*x&`?e1o@8ffHO>^s*kJ!^$N=FlL8PTqqI?Iz524hYVx2JxJ +zpt`x^R;ZjjDbPw`>#^3LkdB|&xiHy$=&59AL%9{0U8RrsiZ~9Z2xpXNOY6o7tA3nJ-Cj1TXs)ygz2$<>AQcq9r-ND9BU-Ti)THQp72W +z@cTxv6`KP!fkMXfsfYNqi7jLgVt%cQ=h=qzcYdAF3LTkwx8>wKV}51=J+{9_+WrA! +zM>Y$*P}JwEaD6l-kU2+n(P?GcsHibpzs9oPa^x>4rmq21{T+FVocu) +za`k#mVlu@UE|GX`kyFS<4T8Ga6^KTL%&z^(%8>Y{;Jkx +zm;>0s-GwwbQvlL_;--hc@V=Y5Fqt61093mPN4X>O`Tx%4{C~qyfUckuOdVN +zmX=s!G2WZuQ)SIWWm|i-oEzOTE|^&%$`FE0z%P6&bLAKyt3S$cB~A6HEShOc0gdm` +zsI5p-9f{;%?Vu_eUQy#1mO!aAsMp&m%7Zn)o^2koNz~FL3S(>Dv9tNdWE4*v6}A$ +zL9tL|Wqqt-lR&tf(6W8`~=z%=Z%?co!)FJsM2)XstvbyqD5Cau%G%-PuG$rhO! +z=S#ggi8BYAI8!~Xpm*@R-aWN81hDmB{WHwn0g5Qkv-Mg7}GZ*^N5}}pUwH4dN +zFBycw+#qjD8VDl#w+YiqxLizKV!nwvnLp*q3P4-gb8$La$$j5Qhv$Lvr +z0vvU?v&hin-+qx;Z3&73uZ@ga{botE-NUdTG-LzImPcmNhK`R^?Io6zPQW;Yfv~Jq +z$b&O0ocog`73=;w#TxS~S0fqJZ)ya0`Laj2UT+av`(B*$!@pWtA4%YF40KHnM1~WV +zrV#j1gK+8jH`$y5e5ACz>buor{#0!8adAmYI%YyniX)!*RgvpbB!p0kxIRm9Th&q? +zvYnadFBxIX28(I%H#InUN?PgnM=|_qRE6gEtyU3MajZ7gAv?I3WdWXY95FNumh7SC +zOn(qZI0%CoxPy9%jVfDNLWJhsIzbLa<$?{FE__OumNtM)dOf|CulT}zYjQmG@oKk* +zvd!fKtPqj0y%T_!VnX}ia1F>%HquEleX)&8o^-@h^U*`s0&}mqJt8rxMpHtBsni=q +z5@@B7JRs&Osa?5`z +zblds!#wp;&oMkWkXFkTC;(} +zU+P0ZRmqw#e;24#^^(ZGQb3~*p@_ts%)W@1k%9QVYbhJTsh9sk!jEDuCjv!@{t47W +z=ayMuwR(=rqqikS*XX3hSxgbks*ncXcSS0uMpA3%Hkei6q>Lv7=gwjIn|=|^faoui +zRc+6*45KI&m_e01%iP!LK{v#?su~q-nCs*zT?j_#dw5TAg*aoU58VTR_)y{AD*8mZ +zdP11q(U81j!?ol81TpGfLd3ztN$-g2)fkQl#y#5I#lLCzCMdG^b6D3yapx!EmD-DJ +zK#Ts$eO(%#o)2eW{aw?qKK~B#INqT4U2?x?sjkRCgO+8ON{I{(Ob1B9ECh>3#=~s^ +z$<5?=AUM9SgLn3Ts5rF)c-^sonC;}`>zq4O&KFpEVn4vK)0))ynZ-LZoBDzJ!QQr9 +z{nJVC+VcVU@-WRYp{z`*fkKR;T!T0;&ijHxeur(vToCnqS6=WM^inlDKiE=$7dra) +z1Tk6Fh0jLhR10;oK6MYi`9z`OsA`u#Y(H=Ceh#MQ6|Csr`IF(L-|JXm*AS<Y8nY<7bU?MUmBi0v@iKVaTV(Ru6j&f?$eOG%bSkj|#iOcF{?tDZeFW+2IQ+X@t +zA+!}0zC#BxfbtRpqGM&Lfuu5SIBep7Q4RS*hs|R-ayZMAb7Vx%gA6^BZGvO{2$W55 +zxdz8EV4$DW=l6?ek%T=C!Jshcm4b**kAC|t{cOi +zM(H3Mh|vM(6y>nM^^x-;TFR9PIXiL0H&4uNX9P%QG6M+q1?H&KpQP9D4;pD?U?Qk- +zw`6z+0G2V19@k5z+A<|5!5UH$1%4rxf)d2`iwd5_v>HkYLREA^fE8C5t-nNKxA#|b +za+hIz-y#NgA9@6V%*K#uKi&~BX6)1;_HO}_;{Z>mWwsl_&i*n47u2%}L3d)bViTQ7 +zF;}A6#zH}B9Nc^s{Ct9H4bH%<;n*3wzKCYzkd#dWZJJxB;JPz4b0f8dEB$GMzsAMp +zY=EUq3D6-gDAP#c40c!t@U#ZkT>3hrtnB*RaW*#WTd&dKAj#!dq&1*vAxsV&(S8Ah +z!9_+tv-*O`Lui319b?q7-SuFvA03Yo-<9P{0lvs=>#vIC9l4TuvRpAKEDj91sn|GS +zeTXS^rKLFQFU9&mR{#>5X(lAKOk=Jr>OQGLPe0H&9wS0HC;6ZPKj3aA7=g<&BDP-z +z+sIDyay1V@4ylpT+BG=UM9~AWjnoFQdNP^~M%Ndj3oGHM!(sV +z(RYAdYPfb;z>h@I9ab +zJ297^*#|&XYlFo|dce<%4y>W*fv1I~9Wx%|p2hFBR)>z-GzkqmOkE(pw}$*ncd1Xw +zPK$-{(=xJPeP7N}CW0>&Kpuxd1Hpl&WQJot3fu!4h9XC@LZzDL3;^|syHB7mVXk9Y +zPTW{3Tub-}mNzyfg$(*Seg6+vK&ZbxcJACk+7X9YEfqWz5HgsGAdgC2S9vAatr=@D +zYJjnafLd@w9Iee&Q^11%+!&rqN3F40S3L2KtLi;cr+Hbrw&?&yb&pGAJpOEam&X0F +z-jO>)L%V?qjU1WWGBL**KywHtO( +zjUZ>Foc-u7`7->{Y1U-_}4_@)f(uGGdBKO(QMI{0K{=W$;-cDM#0hIr|EMFfU8xG+cn< +zmuZHNjXh{zBZGcK@O7#AHlBthZLY1#sQD!~Int`iMw-X+I*e6gZkeudQ;QMSUa3~c +zwW0tcKLA%@3br>u?y{c~5dCZyQ!dGJ;N}o(>Wl0NklOixal=C*&5ANEtAth=Tm}@b +zD7q_v#z0w8I+q=4MgcAa@BC((J3#XuF}yi+s5mu*l&VzK`P +zPZNkbq&x?ubs%R@pHXVYP-j>S3v8E+jkN=~{<p!v!iUPxl-QawAJlYHjTIEpuo*kFDAb|ZWWfv)q142tuUR){pn1z` +z4Kxvpma|VBkTN7Fjwz+FXAXqyOe&;V+8TsL!fxcj9G%9T_0h3#@Gg#Yh4KC9m{ZA9 +zA}o!$w)B<2OeV&G_t~nhs7xrj60pMdx-632s9`=t2_GbW%(DSAs&2cNbM}+BFQ#0w +zEx{-?JH;fZWi-Wzfb-94UQYqHDo7IVwtWF0$S+=Vh#Jn2m^z^M(1aYI3)aMuM$Um% +zs=MGIm-fv@7RR0>Vc1T~P!DaA@lXcm4!|5Kbx3^%Ha3B}1p;s)RV-#(IJ$KhUwgwL +zTz|t+JpN&q;Xzv+)&UFvwgK!wkRvmL^ClAAjhZc8S`wzj&j8~{Fy&Q{t-^OD^82=A +zDoPiOguLl7ZNW>^%*+P1^_fj-V!Zm=3C5TTJE6oL`j3^A$3#`T6`4;PU0cB8jxa_z +zy{R#}cA&{PBC*5>eSQIt5cso=F%pN~8zZE)x3jTHRveSG@aC65XxFp{n+F$ezRjI5 +z1=cXLNO@ym?0*U3A8SYIK?EAL=PtHf5)B}wgruCzfvp@YhBv^?+u!%IhAeJ`hYdgg +zpG1O5r&h*j)Y!gTg+aAj*45%gP=`%@hA;sU^cu=TM$QSfn*p?rKF?4xu#F9DtsTb| +z$M@s<#~#Ox54j9CUAcjy^MrY5jUm8Z(-sPVJ`wV46Vz{_(gIWjsfY(*UR4;N^h*g` +zuUUy-6cr9wANK2K22h&Q%sMhM_?u4Aw1xoTGbQs-BYvfqFcaamIC-W$q{b3P)hlls +z5izqnUd>XF-zjJz6AgWJhZ}y_YreZ1wa$h03)Y~m1EKI}doETuO%`hFNL(!l{go78 +z5uAPBYqRWGv=kiazTAE&SS%I~3?G2)a|RG{?y(pa03aux^!>=D0pKP=Lyp>{zOYJX +z7IL2(f6yH-%z-jGdMi!>sV#5%U{pnIwJbHwK!0#XU>X86{6f?rQ!l3$bv;0H)Vzjk +z9`+DC{)rFA*F5Y>Jo5Mq2Rk9~zV`}xxdYc8gaNE!MAx$;HMyo^ +zh-7ON%)Bv)7F9#>54Z{(M5OO>q_7NtblA7AaZVcUj|SH_a?+N@&!2Y1Bulap^v8|2*62H +z@=6&4B*|weJ-+FAkH_;LeFUU{G~zy!5F#ky>-XIP9Xl{o?K=`GGTe`0S?>(ZLtv2V +zghOQ-e6&_=6=2Tm$K%|FOgnQ7CSCrPQOxgVM +zHVsWEm^0SqKO`xW={L>6OvdBK+@s-3l;WeCHrK&}ALX~MJ|v(KE~r&$UVWcjc3f>b +z6{98smnyj;H7pMfgr8EzFf8pr@3SgRxp^Ve9-LwN>$^xENQm3GYxbz4@xTQENiZ9Pt}&*ilh_#BUe+c +zPe%Ws7WxySSVOJ(xGpV;vn@z-lqS!)YNnxmEigZJKRU3$e!qm%AL +zt*Gf7O?hPFAyt{EjYs;~!i|v|w3-weVUwHMwr2^%9M8`%-fM|=Tv_~V>L_FcC;3H` +zOX35txIcj~3_|3E8ND8ZWF3tw0h4VGYLZ7w6v}|9Y-G6F5YR95A-krA2tJEu5=;ml +zP(MXdryofgFQpDt6DR|ed$~>>Qs1GUt%0$DS>8g|^?o+<>;hny3Fu~P;Q1V>&g9^T +zjzMMN5~g8w6|Q0V|DNX%h&c)45>F`F@1iOIo<;P(2x*BiyDO56IB-FfYZ0Xs?h5=wg6tP`O$mG-peGDQB=|{B^4KPxlDCm~zn!z|<8x-^`Z)bKA|7E&DHY +zS~dqYPq)HQPsnw1vl>s15Dg++F)PkTJ5EeY5Mlf@nv59!B0~&hea4)Cgi7N5CrUs{ +z;MAi_YoLCHytauhcj(X#XFs1}&({nTN+>leA|^1YZea||B%rFh1{I27GB;8sY02JU +ziy#CK9Xrwo2BR{nK^17Z@5Th8dflYCXvZAB$4b!DZNM`M<@FS9*dn8eR^mubtty`4 +zK~#sM5>HOwb}78y>a;Z%0NK_|9NP(<2NXYTOd7ecbr=x|xxtPA_koe0kr`6i0X;l) +zyaEsqAz2oU8CXt1Ed|43flE;?ngQ4n-%<;PkO63Zfn9X +zA#;AkkYKSO)FQr$0R(VDX2@Cs$Yvw{wC4lZeuqZDipV6-H&s+OG96K*@JrkwC5T;~G6)$&8R#;QXGm#=Otf?~>-iK0 +z0cis{b&_L{IuIZy#pH@w6Nrk8-w8k@xA1`kV1b6#vJU|gfopd){d>-Zp*>dj<5EtCV7ReyGN@kRR~G75kIX{5wn5=lNq*QtVdHU@)59S +z8YxRTg8+{Lvh^2c=jLK +zA0zxx*e7a(;Riz+L2v@Q3lT6?pUw7HQ$W=H3}XnS5}#6*Y`@ea@=%gAyYpR?}9wUJ~LY<7$aPRn`_Vk*cu-^MlBi2Dz!+qW?;L +z(dLKdX;6Z0ya%^;ZM{H}3L40bFc3`Wu__K4d&JGv-<*Tefp*>CGE)8hT2r1W{Eix! +zM}V!j+n9p+3s^NHXPO#OYQ}?TRBbI{OcXMg9r=CFii}!Hz*pPnLi*`0OC2jyxRX4zMQ)#*NBI2Tqf2?G$qZj#<&nACNs!yxKSV34vq3Y_4bBZiNzf&dUy +zG?xs`?)QLKSlHsl7Y%V^&W>!Btw_H3NWwH0g{T+`fhv$Hp@2bERyCHPqV5iQxaox# +zB4EBgL*3W_78@8A6${>xp%)`cp=29}tr+y-P!iFm8IYA3TogGOI!1MNnFKa^M3cx3 +zc0qLp;ESrf2k`lFh0H@*M%^6VZ*Xs`J29Fop&Q0X!t0%uEgG15&>-wzoA+Sew5I$& +z(=cUMqc9$a1CMdi$H5~eU;}HZ)6HaHitn=Gm?pW^SbuflQp%2)mb&84asgr@ooxvQ +zG}HTH%O!OJ+NPgI#HorjiRr*unF3c0i!fvhFeCn2eM>M;2ZbqAhPf6}vd~?#MO~UJ +zGB$*g$)iXBIti5%EKbcV{h|s`s@g{a)g;F_pSJX=J;lUxkp +z12F))PJo+4v?u+)c5Nov2p}mCaj-H1zkmJAqIMZb9TIvu^?0pE=5@@vHRO~pcoIxVrG5j*bCkYE +z>Sv&Cjx?M^NmaE)&8&%ewq#O_kg7xyjKH}LE}}&u6oB`O1 +zK!af|rWrf{Gyp(4TJO#W^z89*aAvp6~2!&sU2$gXK+sp$um^sWnElkI7-D2lMuvjS#*Zb0&6Ja +zw1`2_xBwI3t_>J%w9p{Xv}DWDROg~WOdSW#z}fUOa}ts=9BOszI8B$6h>liZL^GOg +z8JEL|B(aKv7H9%R6ej7kS)e}DFjfLo^E0@z`VUO1W9i6x`&2f)z>Upg2{%yDD8Fs&{2zQ1he?6*U(ub_$ROl)8&y +zQ9&0J1k^EA;EuaaA>VrvyY&=yb_XP6OJzH(BT5kwH0HQ7Uy1@*nN@WL3~<}@Lm5J0 +z4&wzXC)CQqd}OL|Kz<{w`Et6@Ar#2=URK8bFxlZG2OL~N%QA3z3Esk~BTMB)}Fi82bQYzS$Kr?B5Raz0_3TW-IzO6{$+_MKU` +zY|DTKPfQy|*)W8ec@&CzBzv4p7N0KQO93}i9OjxUGb$A!R>3=mj)jr}q8Z2DpaDEi +zquKZkFwLOesyR#bQ(I9Zqa53!v4yzU+ts|Fg%vB!MqYr~`gZG|Cak+AvfldJ=XSK~ +znnB}1RDn|$xoZne98$GB^$yjc`gI~{0F4`5i9L&xlz=CqUQoQ508-HE`fE{^w)h%t +zdLj9=@hV#5L$uU+SFgVbir!ZZ6A+wOwM$fb%uA^P#kemmgr0*cK}%>bP+wa{?MkwH +zJqQ4*+UzUsSIUwYRG^cCTBgT=$;Y +zrVG0dXX-u##0Mk{h#wKRvE6e}irWzUF{6*@Khgf!AV% +zwL9@1w&zEVrYkV)h(lp4!l;=z?ok;)PXTYgQv|Py_{kit +zV(G=wP{0iYH&bMy0fWv()MwkBO2KJCrP8|9I0L=k^!nXfYOaKCXsroWy>$BOhHXI3 +z^n^RK`RhV#Za=9YuQ-d7u-p)WI*%|Af+G)(!iZ0TlZi44MK%m6pkbys8}ERp424{^ +zHdjDQhXT#@GHbf}1CX+-D+gEJ%!6i-+~BZR9Dh5v_WBpOVr9mjHSWLB=S@h25Gbmo +z7|~piI}j0g2hxJi48ur@vBxQU@A2Lfo2#NrG9wE~{k-{-O`C0AfU~s$oBdel(iv59 +zNp-CixH%h4UTx2&I@X;7=c?VkfnkGqv+aEl#BFiI5#kHO#^X7U-)`|Io^7}c<9YmW +z9tI0edM!f00YP9q0jA4%W(JuUQo(7$hlwDzv!NS9P@^NDNSMjagqx%RC&_Hj0OAX7 +z%*M08OcWO^ph*0HXt3SQDN<&TgyaGt)tHTnK0BZ?25p|NqT)Jgvjp^5TX_X&3u66Q +z8#?I+NZakJpS*b}T5=XGQauF5#MBzEfeVO?Fjyug$4~>!27CY~gW)UQ2wULn-b1mV +zuwc8s=-hs-yPex*E#DdI`z3sH@8X$3YWFfG-;rRE%dkm +zYRwHS_eT59?Y28o8K_iupU9F9dJ!EFB}uQ;E7?w#vc*FHRe$%`wd&o$T(Tjj3S0@ +zghVQWpJ8HRc9A3lqbqnRBv(*3pngClm~o|w;88bG$dDY4WLa)XbN5-gbKB6Rc@}Bx +zw%)pnQ2HcaA%lH9`Zv0kuzaVz-`sG^LZizYMXmmXc+`(%8Hp-n^{5UMb!=RK!GR1Q +z34FAuEA?pF6Pd_rk)B>bL#S%IQL9y>;Rlse)hWvLayqZSnX6UhZ$MtR1KK9WI=(;m +zf;c@>a8Y8MNhvXo8**8yYPb8BUPr@ThheOk$m;+E1X;N-YLH{iU%oC3nvk4^t$Sj;lV0YJ! +z`NfNjB#yVgV0S_A&s}1x6Yltw96Uuln%U +zqhycYD_#c}6EXm&r;yDYQkN}or<#o92Es1DUz>W1inMSF+Jx=%cclT)WMloVubH&X +zw$FVH8O%{@ZPdH~ExUHkw@(3X?T+yPnysHw^#H6Q02R7#vmu==haj9a+s_*Ii(w_J +z>7_4o>=Hq~eB6LRsi;5B<9wjtx$pir@t^+}|0&xsFnUJ_g7c0b&Nhx5nuBwBSh+3G7kfM^ZB3t;(-+h_S{^kE_n2yv8RW6%}P_d21{Xcs* +z?a2*q{T4vFT&rbCb^A&sqlv49b+9sFvGD+O!Pv^{K?7){AJ_c0E7U*EbAP@*zMBUk +z9SKz}My)Mr$p+NeQ_$(UtpJ5hx@w~}oS`Lb==NAuJ!?OSG^DorxW%(MKH)dtdYB=chqb5NDu$L6KyN-{tNazVVZ8P&zm4?`|V-0`-1-}4!1Hy%E2orgiiY} +z4E_9b{jE!vALY}Z{~psXp0!<-0ts7yWlgb_YOA+b?X%_oZs*>3nH);xelA&J9B3Fo +z-vjAfeQDf`0o;a^Mn3juAL1kLzCyv0v9vJ@>z}J<$ynG_#*(s533dv*Q}`dAdJ^%0 +zLaoKnk$#pPADe@nrrx&lmAY>>%px57ZI^~Gl=|0-giw6<0*G^zy2zVAXpyk%?7!MH +z>Pon~-FDk&5LrqYq1q{mJ^%kIt#OlH9SG?mpoYuDk=yk1|IKlAyG +zUWXezDDcqbz(+ptZg#pw{Yb*u4wHJb+_wR^RckZ&!X4gd)n` +zuqq0gHrk*871Ih@C~6ICQKyvZ77ETYI2avlob4oSH(Brg|I$rZ7Hw<#GRMBw*7l`T +zf2TQ2KK^@e=d=ItLpJ+UCfz}FxLI+2u7o71K69ZFl8l{faTHBWwne;xV4$kc)X!QB +zf76*OEfTQe#`H*86SbAN%hx7Qaol`nuYt2PwuIFlzL&E(jW9HRV#d +z|5(CLKQvg&;%3U?Nno{+0taUFYi)Oc2Vj31VsEOQ34sP~SmcuL<-;snu%l@(r7P^$ +z|7)!u(Q%}8C$BzV8)S_`UmuqE#~T0^DRni)wSiY7GL%yKjkX=XG!?!qs?u7eUmts&O2+z_ +zGvBSn7aNPJ;R~r0QqGo}k40^KJuLoz$tEldX9SKt{K>z1kQk0A8q8(rJOzi=O2{qw +zXH)-;*DiG;*Vno>4;C|_TEA6GTrw{=^I-kDsG8YP%@3{ize+$|;4})q^XNNx4xrrQBeCvAFV7S95gv-um@g^F@t# +zg*M;6EZMWP0TB<{3vac}3_+Iud1*k)>b+Hz +z2HmODCy3EqRSI~UOday7lG>h^n$LGGdqV&KAOJ~3K~%5)P8ATXlz8i3)M&`H*ve}r +z_58oFH6f5NE#QOiyvV~3zmcbYvS)kjZPLyOm-dhoXs}Y|wPcxFfq>dfF&p4ByQJ-r +zrUuKRCPOy~97?0(TgbB3*aI71=R7$Clr6{OhL3;r-Hfu}C9ftLL$OK!oo7yY{BvI? +zj7mC9xRjBc$!d8XBxi8hu3IsdWXr4Ya;*ig$@02jF#x3$#4jPdrOtU%d>kPJ;v5M> +zKrxe9RgJo?Es2gdf|vIOn4baIUrW1VS%Dis(XyMm5PHpkHCd>^$ZO@Dk~{dY&VOZ7$6-sv{KEaljw25q&4FB}H&)Pd+R22?lTiu$YK>Zv`*VCmS0 +zfE*o>PEYu)cfN)9z3nmubH%=z3EVs&0H65u_t{;0k#MdiRnxU+rRpxaikqnSVHA~y +z=-bgf>as}-g@w(uvfFDe081pAQ4OZ;J#)A{*Kp%?#-F*FxRwzzl+vI=IdV9lbt)Gog|* +zB(;eUSvzq4zu%haqrmSydM{V*x*Hj`xM7Qg5pko1M?1@lAh^11RXTNxw{OCs6M9x? +ze^b8K&sUxR3+1iBS$B?N8`miZXFgxg(fU!-j4c${bD=jY8W~Dlb5^hQ-<* +zLTRw{(%~u%SgF7vgSHm-v>k;Mur@R4hgL(7{?RKxL`OPQb +zjh7t1{jN9i*FXOi+b}}I0vb4n;Q**r0BYb?TkhJ2tE&7fb8f8#*v4prpp`2yVO)70 +zxawUH5)LnpT@>!W=L#Qw?>%G;`F}O4f5bE$0Y3To5BdHNe#AxDGndIKS*M6pO|<(w +zsT)#Fao5~)eK)$%-?eI5-#0RqaU)CD0v2j-NB7}6uNieM<(#_~&@hKP(QZE{-~qs) +zY&IL_7|lWyhp$y{TZNN#ezPY194pUITl`vj2T7r$vVD1U$qXHJRuIW(@u(|Mi+fb- +z)AYAE6NiAznIT5BMCN3~5UB>DicOfd!?cGx^SD#3Z8UlH{ARW_dmE +z0L;!0w9KmWY*toL#rq{QQ1f**f2MR*@xX+?e#dQ#zW19P+Vx89?A72&=@V-mGrcTi +zJ66O(U}J$f(S>;S#tQT+aIpP7ORBl;{=+6`o1jeyoK+srwzW`d*VO*LT0zh%8(mX) +z*Bdsx`)zOJYhQbjVcZh(o)`;~6RHsx3R=}>vx&6}V`*u+)S?M1Cf2n$8+S`}ZA3#~ +zRe$Yjzs2}lRG)+`qN +zfY4Bz((~OF)MZK$G!-O7vO4A%+0V9kW(Tu3PJowc>ej#29Ze9UntNv;)NADoD5hoX +zfmjTK$O=)b2FeXK3zm7=v?lNDdYr(#4b%8W3!UIPE{_xTJ`P#RBgqCZF-yuGt +z&ZEv-q?{|3wb+zeQ{C%$*a=sRz_skI440NVguRP*B6>$FD}mA^kq*HRaap>$zj_fzL+ +zeXqw;uyP4DQkpJ2fKtRGMms8lU)8DZCd +zN!KPpQKT~e8mGXk(Sy}Fk_P4?I2rhZN8iNgU%i&oZGQ*fc=AR5>7SoKa%A4`@o7(% +z8I1{2hLURbUiyO9MU7ch_=XPF_iu;`&@=>AUVzF+X&WC451_};=wTksmTfyXZGfeS +z=$*g;?g$1TRgEg8R{QBAjH!GgJ#Qe;3x%`<6c%)H$ouOmR9EAd>4dh;q(0HM*qeD! +zt+A%6HFXW6V1lI}b0x8R=~sO1n_oiG-JJJOE^nBIiwyI5&iRRJcMZgRFX!fC&Rxu0 +zd&7N5egh}o@%%eYy;N?!GB7NGKUWic`NweO*M+MWukz~@xcJ=2CI2gaeX=L*jyRr_ +zA?&!eRdl!II7eiROoNC0mXkaYFBVRRD}4LvB|i5K_}Dv5vw&L{0?n>=z?F0G`|o)Z +z|I?3tOc@5U8?923IAj=bx!_P*G9cUCERqdrZorzDv=uY$UVtTuh~g0!@J>zbV#C15 +zuyMrmC!m*z*MxUIa-K&YK1VL5<=iSoyCMIvn5*|wpZyN1J7a+&45bj=Oe)}0buzE& +z6G*$qOy@b2X5y%}sdsoS-BeiKS}+j%PpZaxkPCsuW7Y%Z>WAGN2mw@yDba{Yx7%*t +z1dvs6P!^5AfD^+7n)=e2fGHW^Q4@JC_qx&w+7HzC8~wM{gEQe;C!3nMIQ#q30KyK+ +z)QPH!kW5oZfsM~N+2O?ziZGN@oKps;Y^IsO3v8GfisN{`it`i4;m9z>RY%^6Mb=dR +zjH&!rPT%2yN5{QVYbHPtTPz=a?-l<2KVISam-hHEBXJLI!Y9y>XlcVmwB{3Rp{!IP +zTo?MmVldRA^H*HO1U!ff1UKN_h}({A$MfJ$$S&}wAAT1XkL*;hcK46<=YR0a#N*%m +zDd)y5`~4~Sf_mv8zr7Gr10ajduNK&_OwqGisdkN_3AX;a0t$V%Uk6H5?cMoCiC)Br +z0;O_j0Aif*ey9RsRYJViB7E)bj#LJ+W+D>28=8a3*ydj{^{coV=@m2TB8dWVK!J=9okOlg7!r;>dncr9 +zro}9$;5O*52mIz&?8?3D<4D9NWaiccKtuZzkRs$$xVk@Ozk7i^&BW8lX?~H}&6MmQ +zDTzo57&@tn_-w8@HP)D>0XC5kGGBe_9AEqC-F)Cp!i-@Vx4L6C0kBti@UFmnf8z~2 +z_JwB&VFW**rDoZwQGBYdYG2UGX;q5=+eO93;DE8V(gG*c3}LLMNjv|dN#Aa} +zV;DeN)YDYGtqS6-dTxseF!a}TETG>|2SzFeu&Ne}uA|P+2AWim(6v+51e0yjDVY8S +zDmB4F)lASCa)456tZ7rdA{rA;C$dvgu@qXWGnA1sdR)3dP9t%?n_MibY)fi%vxS!H +zU8L%HxcYqI=(%683%_7D&1_#%3Rjuko{|DND_$bA2g%+PN2lbP>9(L~8qhfsT;bZ) +z7x}Ai1b*v*%NFAh7Rv0+TK6c3*~}k(@BRGcKYfFg14DLXRjXiJ9L+11i#c5cr8vb3 +zaL`$7S_6iFCF@TG0@QSBM5c^~u!JZ!@^vG!%)VM(gJlK$^^D5xb^Y6oTGh&O +zbsgifl+0?S!B-%ltHpMynsRdqw$|cq+WNZPb_enSR7qA{8AA08l*Bwu2W%x1lnd~Y +z%FX!jmoe +zrbIL)LM>ySOIK8GsVVeem!jaEu*r!aJ0Kw`;?oZDL`f4mPY`!#*-_?+GVLkxlpJ^D +zd5=z~l=($cJSCm(*j{^N6vQ@0^No(35o-tY5f>&y1YYQMc +zJhnBdZXC^}YsZ}#D*`o+1~{`>E)3gp)Ae)ncH07=lwv8FT26p^kxe5oile862s`I0 +zbFjMUO6~%oEp#|s=r6yxZX^zLWg_%`gKGbqtf)yH#ZYW11xKo&RY6KZoRNYhXMCv( +zUlkV%frt-gf@C}0Es=Msn4(kx0C2o1xUCQfY;wjqOUWfE2`M|o6|O$F1#z>s_67uj^Beg1dmh1Uwz#7$Za7C6k7^0C +z5$8ulEIDdl?BbAGQr(RE`f#ouYoCc&T6W9NI9R#$Fd$p2x|Gu~zw@34c;}l2v{YUO +zH`f5P-fJud?EiWPpZdyEY-BQ3rC4e-C#mX_(j$=(!Sys?tw&(t`E&i>8w|Q~qm8Ov +z9gCYZrw%E^xD?lE+H!jyV_dvq_5C}c{=*&O0f6}~S?%uQI9AiU5T~iCE6P02RpJ>V +zEXK%gx5If)*n(%NO0(u=FX&QJ;q8+JQHE|J)J5KzY4v%j(PyaHeWHzkVX@dr93`3y +zDl5z?mK;N8%Oo2;I?t%vvET1Vx!A79vbb+HEQ^pdciMbeO!+(F* +zMP7LNd4dG8#0awGFMj7u +zYzA<8>*{|Kc5^n_^2H}#310V1^NuvnXii2^F9p?%7RBWjGuqgHhmW=~*-qcs +zfz@b}x<{yx~Oip@3l$SI;JSg=N(k(4PhnhIHgxLfL{^%F4xFwKc!+EZlD +zoFh6V$a`Xll-PIxiR|bgAih{-q!^$?0~L!Bk|06h1fKlSulUTj9^~JAXv3V%%a-Kk +zSU{r`2eF0sh6}<+-}@H+=N~*x*f=r)Dc~z{r__{crcs%ms0j?4+XErRXi;6_mKf(+Mm*)dR{&5-X4*|e +z*<0;1=Ljw=-0|ui5NEb$i%Va$1M1>x61Zt3%?(r43T>FZ_DnG_k)&!Fc>!SV6mc?Xw|&1XKi +zTeJV0cO}+!`RyNFE=zB(VG3L +zYX{IkYrv9;q)058QnL+fsk|9ilc#%I$T3ESVK}I>33MQQZBhdVn8EF|J7P$f+qY&f +zsm}#tFiEWq(Ce91Qcz0C)bYc!bnre#V{7epr%cmixzN|>ac$QVh*)&&s+O*4o88^0 +zi=aYVjy4PdRz;k>wp82Mc3p9}Vv}1}V9{&sG*K9b1RKE-7z&7srsj9PeWQIUqJ~nJ +z_L@j0lVd^V8Py$$jGGGQ{hseW{Y##B+F~%~8uENAd&Zg*pc&qIzvun$dow9qVjRv9 +zwi{&J;M@l91|)d!)vm7*Y*x9)82e@>v;?0fU<+31$A^Fp8wP)ladU!hx7>5jz5MZe +z9-!n`Z~Lcgx&BIlzy17=c}v=TlWgio&!%B~3fh +zG!dtXlqV!dvZksfWYiV111*B)+%W)6m<=mpw;3m<{V9Gw;Plt*RC(;5j`^(z9|VIl +zx?Av-B!2?~y;!UP4nF$cyZQ822jc8&e1(uyNR9SSq2~y)SZqm0IdRVtQc6|2;^mUU6w4sPq>8BnzI{+lKM^r%2| +zsQ~~S#L%?gzuk6cya25Lh}HpDwdz0(AnZvGmxqBN>;35Euv6(BS+o%6nR#y@+&oVf +zcd*VRw932Iasg_~(boOB+Cz>P|E}Qzs+p{Xk_zOEO18ZsgDa7wGxJ=?`)NUO+v6sE +z)dfX8z9`K50;dzXoDwxsBDkDUNp>AMqd8JEnt-jZb4iq}xST;FstHX6;zWw4#Cgw< +zX1@5XAM?ZK?K+!nd@Fm0gPtrkCwSk(Cp`Sd%k07c{VC;yBnY>@&~d%8r~T +z%ekKtR#1JJ3q>t$yjp4=S|FO~>#dZRc5Vu7{7NC^$UIN%_oqzLjycYUbly6DU+Kyl +z=YQ=1P}M3(%E2NR=g2Ad_g)tuV3Tw$`6hsKzNZUs0%CnLB2r_T*TFs3r1I)|xbT@( +z<*GM9i;{ArNV1g9J|KP|)a){ycP-5a-fXGn;aRe|R^K4t7Mre$X)n$^6nF+7m|S6s +zg+U@x(rdQ@Ds++NOvtkZGHD@r&nS)(gsjdAcWSPi(E>Q3RstkA!3D_95wgcgA?FBb +z!iSj`PleBX??vAFp(`c;!jcyJl@?Xg07BIQE*`-jy#GPI_KW8jr^KMA6cUk$OWAl4 +zA)_T&1!I8{0QCed;0wtKNFY$qQ7C?c*A0H~_{|Bqoa6oPe1zY8^9fpD2)7pbm#7(? +zvI*5+T~$8$r5|xJV*CwP>R3`srMsR3z_gZz3pEja$eQR}6f9w==1j>mxq6c<*W@8y +z09{X5;V#$fiB|Eq90-o}qF+>h!_Bm%23z57x6^K415jf#Ey)Y>LEsN8suI`@#+ntl +z*)w)fecaMMYJZ!M)uO}{iPN6_e#d^=6X%(fV=uO-&F<6oSnvU3Aubt%$pzKa8G=p= +zI3*)URGh|Yi?4Qnz0vSqf8}d!lA?(0h)&5-k(3e1MsLoTo04R}YAT3E3Ka#;1?p}l +zmANLEW}q>maZgF7NS^uB*Ph|o(*+s76)m8f0X5t>XZZbh-pl=$??yHQvOPxpkzoMN +z3U?qiWtexhe79w^rKJ;-U>pD!5I0!0;vtZSEpSW-TmI}L53mi(Z@Zhg|Ew2m@yBAv +z|A((X!%v@i8t<*>rc4nuTHsm%SIUB|d*VD1llkSF +zL31mtm6uzqVh;cMylT2#bJc@v+HHf)rQCFt+-kdhH(<5>OLgsuSS*0|zS}g0U~5M? +zhhf0`Q0obW?RcHC@q`2m8U!|ND)G1_-WMTA$?>zNWKKI=R`A;4_&z!9YO1cHr+EKLt +zXMOv|OP=>Xd@q0bgBQ4nC*g?I%@vB0rD<6Nh(=;(pGbu$%PLzuBS{!?!D85gfn^d1fpEeY +zw#4m*KmOpGx#xUM`Bepg<8T95x3>E?0U#d!>B;AL;zvJX@PfpM(+QQdXmZshT#BMO +zk@HNPXX1X(Jne|Hp(_1SA)$3q=_CT82a#*a}Bh(GpI +zRj4*WP(2NIqTPYYK#mj*?Qg|5T&-u`ngY&A_jNXUa7$?em-n(@^U{&q?p> +zcGg7`X#T@PTTA-$E@rf#B~h{`V~6QE9nOkMcUg_8g^cm*n9{apegXaS8edEZx9=^o)zA=J7VQ>P2lJjWe=RKl|ld8T3k5kja2Je88 +zQ6O&vGM-~77Z{E__uh9mfAPM1DZuD&jr*Ue0#e>5uG!_k7ja; +z#A*vknaRZ#yA|rFJACb9`?KIdtlyXQpVhpSievS#-z6=brKPXo(3QLvV+E4SlJ&QN +z=MCgr2hR<|Kv5$E4a4gp8n82!R#i^AYQIA8t7bFD87DP%;2?_TjEFq647l!TK1%pDKVodL77Pv?I$*dP(4>w2u`|h7@_g8^${qP!}|I&8}(~h`%iM+pxlpQHg +zl*x!dxil=GF8DSm&64j6{tyiAivHrmTa9)(b9HAX@5@aH0E850ang!e9d`ok}jhmn9sHDVojrJ#GF1D2c38JmRG&2 +zXOF0ZRQ;(|tF2&>D~l!GK*2^8w%ylW=LE&gkR4f|Xe4@vhL_mx9dSRC>n*%$bIjle +z=KT)YPvla`PT44`g;Y~!Rg**)jb@25TfN3S2C9&qc{C&oX%A&LAnt_Ee(_2Evp;wP +z58XW&5Lh7JWT7=2z&pbZb_ss>H!ko$-gS|ufAu1`0hJLady1x->&_N_R^tTtdVQ9| +zz!Ov`;>iSZ85o9HKqFe6YL|LPA13y_rHIFUq16Q!g!5*?G(Si +zhGR#@gsW?AK&=`4cj5p5AOJ~3K~!VB8hbrh@T=#2!7t>XMHJh>cixJulF+X9sWlVg +z`iR!jwcO?cYzH*&S-)>p+&EgH5FZZhir3g~TZdSq={YZc%(fs}6jMuU)YN~#(uZBw +z&fL_243I@Bm(>9)Dcv{ygA%n%moEU$>J(yUc9`mlv)3NQc?IY3>IkC92E+}F>J8Y6 +z5F^C7KJtzCgRUX0HV*|H8%7it6g;>h1TBbLvC(p7iAC3H8rbVjSZV5O$)LuKkX(>F +zQP@Myy!h;|`OE-*K!LxPe`12CqUN{a5m>tf1-S2m@F(wk6L~m-qjQKqA@~v53=l?~ +zZ17EOkO3jsCJ_SSw*#UigUf5z=UMyj>}Iu^ +zTn(vRE2eeV5ZrayeVf_WD{irDthw6$#-oYcZo6#(AoXHOorISCm+opStA#H$?iq|H +z?flF9yl`fb-{42<6BE)4wRbTC+A(_OYkKhL73=G7IuUy~!Fz|t6TILChYuUJY&qG$ +zcwvL9_u7By29p--bu5+vs6S>XQ5i(fO)P)v4v6R +zDhN`w{hf#Z@U@?D?R4T~3~anG1|fJ`bxsv$l{t)l47Iqi^l@*0y8vk9vYrx5mn_lP +z7tD)YKVR=A%Z%uNbfd=~&PXV_Fl5)jUpr}TWxy)U9$=m(;B}G_sG*V#q1^9xc;^pn +zm~IwSw}wf!7I${oA#L&M>a{jizU}e{N3V6QW;(Rz_2{l4w5_Fna|P7kiY2pH4Ze}Z +z%tcfrkxD>QLVRSdM3A$1#Va*k&8?!4Q)W7SkvQ#$X-}GV$UKwtOwwo?QnG|4jo74! +zT`TswWw23kNDUm)npxLyj!e1gknjRw%zX2yAM?5I{fhtiJ$DlmL|Mo_%OW@!4cqbR +zXgM|3?8Em2-u2*R{{BlZa-xD}N6bn}g_07YnYk#g%w}^dBpl?3Xh4%=p5UJIBOiSG +zc~UgjU!(rk^?7Ejm*L8#Ci_%){-wfWk3RwBMfPP+i8C7aq*6nFH6cB~ruq8IG7txp +zU;Au{L~R>NiyJvJ=d{nPWnQi3aWeweny5mxIBWLbx~n(FtaHvPY_&9cDb-jDBLl5- +zYu;|VBjLb81^();Wz<_h7Z>3Y6VPannOx4^z@naC*Hr2HTg3*hBVhF4qE%?-T19Jv +z(j<4&9Ha$el=`+kZ@Z2O0Y&p{F2kg_DK5z7YwNTcYfu=bnIbc(WI`@BX(d_usFXpH +z-A}3a;c9Gs5i0jNR!tDBZMFWxD@>}*wS&K`=GDzUT7OVKS!oF+7cwpS)2aiv@L!84s^!7% +zKsF$_6HLI|_|(Q*Z(dLl>5Jd>i(VBzbkfs^Ef>)g&cn7g_vOqda^0TAfys5?yQKj# +zTEDwCHNG8tcOQ>xo+%P4{m*vwaFDK@HY>O}AY +ztBX5ORhOzWT904M0W$>(;*#MTDROQseD$ek_~wsyIA@C}*9El>bnE@q)LvP<0fRT$ +z_u;o(;H?k82{u}DkEr;B!Qb{>hIdEHMa{dKjZYH-pL2S7F9L>6GZ<L3!&cwkRhdvinBdHWL8K5VPA0bk$pz0bdl?>D; +z*)oyaVwLLokXi9%XIe&7reD3pV_*E?f=$$gwWc<&n**fn%-(PSCvaus_=|Vk&v4-a +z;rv~U!)3y-!3_iWEnWxnWrtvbz;6hf0LLdpy~Lw$zQTLntTraf^=z1SAEqfVA1ab=# +z)}~n$eIUZMh1jkBvEx+GPv6f6VRS!l~_^Z!+gI_=UEYr1L +zGwog?&J$%e+HWqFjapN#MfARr3wzop?kLqz6L<8-dycq +zg_dTz0a#6FSdRN#0mT_jJDf&-@$={T)E9q@3k#Dj>5a_*SU|aM22_sj!*>h6{qSXS +z*}`!kk6U148sXqUHaHz{!BMs&C7c7@@W6c+`Q5i!wV%N+LZAVE>)pSt(X2I~snR*V +z`;$|?{FQIB8Eb&fG^2SkYP6a}lTux#Efl;9OalRpMAclWtuL4UUFOEBBiai&?4$)a +zyp^^@{Wa#S#ZXutz;#?eSqYFk-EQBHP{FxY8!)A0sj_6LaN4S!`VgwNP$Qe9QCL`Y +zrZmBLxX4#F)Zy>9&({WWm9ioSUu(}7dM{l|X}HAl3$o4-y`T-=>exwMD!jh7elvA% +z7>?P5kq`!42zciWd+??fx_WJNS5mv5)jf8=Ob_ozn!Rb)>QwQr;C*2n9slr8-{*yE +z#TI{FTx_b>y9L{Ccv%93g22YXpZ?|vC&!lv#}^roFEMV;vmG~tqb+VcCTzBZ%`xHl +zgw4?v{PrS$`mX!A|HLgULwWh#zb*a_09O3#6OaFd-EPMyN+97}<3(6pu&CvEY{O_% +zqpjvc?Bwi5VHSh-FCNUg8?D1L*WJHYZHYCYDFDtm0B6zslx?GwI<`H@P9KLmp!cuZ +z?F)d)v^&fFZ^&umGlvj*wxD)Qu4%!RA*!F;v3t}5;N>8$;EcJ}Cd#toE^2kl(prk8 +zXr88->2yy{`L?cAfRhtPICsP_Y#GBq7y{mhC0D%E#aOT%YYmAs!GPsJ5Lxd+iiNyV +zb^945;($@%{1$%ls~7mvlP}0 +zlzqa-Jw^A-F_Nc9$rCwS*tvOY@@l8IUA}Kb>z%(zoRv9A)2P}-^@(z3G}ls7Z*a(& +zrYI$+zWW2@S|^~bt94?Xo_(lx%sL70Yi)NV7N7;itfT+dvCUwUwdDzD@d5`oa9~5h +zp>Wp4tU7bd;rbI6*GfZ8n>ta?q|5oFtN-?6g@qa{rI1xA8A?ewiQtqtjgWSvFpzR? +z^n_Q}x_YY?wO3lg$eJ0pSj~b~%mgW`KG4=9tKF>KFQ(=eL9tTkI5P^6jA%rrJq)K1 +zu98#cu`fNzzk1gdHnI?FLa~KtcIR^0uFmmB3JNuD>hckM_>uE``Denh3SI&QPtrsz +zf~do(kkoU!hu?nWn77?yQ*ZFsyZzQ2m +zsisrQ*?`i@nv_-)h6HAAJJd+pWv<8)R__iqf`j8;W4kj{;8I9E$%i{gZPWm&sj`=Pas$=Hb6{tp`%7NV +z1i0?$>Y(d(;;JsM4Y-4as;g0~0^vZotV{rLphgWQ021)H?#g-1O$20vFCb1)X9Q$d +zQ8%r-`*IyGbH7^ZgjyzF>l>*&2WY9TjTlo>=2#Q|{M3(m;^(IzD==_n2AtIb3{hVq +z1Vat2_>*^@Zo$V4A>e@1a)CYw-ryxSyqoUq7YJjP{KV +zlJ(nX#_PZYuo>F20k!qgFf%!`vxry~A*~zSdXv4KcKap(hYTibCdpdhgMk}#Z@@ZH +z==DhE^*3e7Jy)*7^={F3jS8N1dwF@jK6AID>l^)5J+x(Rb+t5;bKWSy!Q+R(5CX$6 +zupPH}?`}&1h(H)UZrtF!MS@9Xe2KVz4bmR<+Qc;*e|4fZ@hZczsq2O#xaw-ERA((X +zBHm^8(~eJm<7X&KQjN3{0SX!O;~qCzlwGF0ehm$oAYNHpiFP +zp1X)Yxr?_xbeRvnHJIzquT+1x8mcCPSE_$1wb)01$De$bAN=eWY+Nz-A%d40f{b>~ +ze@pviBiu4bt^YO`-=T|G`jfK)VYML7cFOv@tG5sEr#?rQ1H2y$2V155b~TfBs{sI2 +zyM|h4!x3YAeF%Uhk%8d=Mr)M*x~cu9PFhZYb&`unV9BZH(J=SkYSUaT@KTE90w{%?Qm?LcyY05mFi-oAIL8=!Ka>^er{xP= +zvjW>quDf#KRNKHU?l)b1MvoNhk6YY^+qoCl&Vm7)%@qjk8mx3wscbE+yx5|d?h(92 +zEkIU}wx;JPlcs2bAXzH2qD!S@!>AU^f4OH4`$rpar~N)UkD*sv&g2|PX(CR0;{Fl{%@qpHs#ym@3wyd7vQVke}ON3_4~N_lzD%ZH0{W7qNHfthq9#cS~5g8 +z-+vVhHFv_Q1A!V$)%v<+k?!p*2)LeD>*cx;N0kx-7r|fe`V3j?2`lapTkvkqgFdz +z&QyC@TxyG2-F{1io|8(bE;%n29*jGj46o7@5FiA>`(ri|@T0&G5E)GJI|1=seYb70 +znF0O6t#`VbS^KK{(q!dUGpe(zDbGZ~=R}Yh7YdI({tUmaQG!k7?pVN?du!)xF2SMx +zy=x19^p1JOi9`K8 +zDiK$5ebq*#i36&(e|DkE4PSPx&aL0BgK*to9-%49>AV87ZengnYyp3T22m0$N>o)(0MkqK{pJnP +zdi@GcGB}S@;k;L#{^hfL`N{r~#c%wG^BYIpSzwQA+<7gC +zlnR`R2?1A6R)s?|mJW9Q1`4jyf!U7B!Jyh6q08K>8_9b8Uq1&{^R~aWzS*|nNcZ`- +z`Pqa*7={j{TSDD{J3|z@op#$AfH+g>N-nj~N6i9M3MnlK3$&`IjV|m%l|6d)G75bb +z>*|p9UpEg9>6L5sYZ==`9lq{)XMEM)YURXBLuFf04kI|_oXIJZQY2L_z#I=%up2@e +z2c5NyzXF>LqiD@G4C-QX4bnST_`@;8`;%dj$R|W$+7T}jW0q?j1A9%#0=kH>(xtmD?&+i@Q +zz3@ACndk~H2mZAgpi9tCPx$HJuOI&*&pr1Xq0DHWAydo_WsKb<1#Tp%dkQ-C4nGhe^&YG6ax3@T`tVkD)rHq0xppc`mus}B~ttJz!&Ftf~HZ6B1C +zy++*~bv^iM6Nnb+Xp1OoBBxBAW~=#j=9={E8*rUQ*vyRJgdvQStf*MQQ$zfH_YXIo +zO20*IDyAI#b~mpMH#R!;bsZz1*@Ro1$?=qVKEaR85AMmpm5|4fEJB)cxiaX=_r--&Z +ze>r!7t*crPYwEA|;vWmiYndAjls;hDocKaGYo!ugkV?tzcXDoKL>-XJ2kDkX~;1Ei8i`IX}-dZIDWBKIp +zwXEM>OyT8RYxQgO1B=SM3_wH9MFfxFo#H%*_ju#P +z9c~zKGFXXd=MW!oQiG^!femvF+Kowd@gXgS(N+7B`W`*=4b-SJp{uV(4bC_UK_Kp~ +z@ps?&F{&$f;LErKnJJO>*mwV8 +zQGcKO>eD>)^XE8niX$TA>dveglU(H}tb}@x*(=wm(X^s|!E4$;+XH51_Ae1NPLjmH4EQseU}>}zeqz6+oEd+>WK +z!l;pHHUaLtJi_(}?~e&CAT|B2G~c~90or~4b=#CyyksS$q&nvpFtbh0`nn1ZR|G3M +z3Zjld6T$7-Y#smj-Cyw2tLEW|D-PRA| +zTK$&={kI-9A=B~`wD^Qy@07oJ{QqX|O@l4FuJf>O?S1Zj_w|Um2@oJLkQ6C~lA=f? +zMM@NDITn>TqDqSWBNZi)oRnRuauUU^q$)pBQ8`ML6(>sRhbxuIcDXYANJWF?h&C;e +zp#g~$2Z01Y%wwa0p3sf%e#5=zoV{0mthM%D``r5)4ZM~M2e|$2J;OeGSo5@>#Vo9V +zki_km5&}u3B&qhrQs&*((Cjn1A|=R+S<9$Z_%RWyJU8|2wh8`qRk{6HVN3qm1kIHJ +zY4mJLQVdqbf2$5;I=+vuk-{MK?urDoK$XmOlS6Vk3p1CX1uFoOSptP%E)g4t|KTia +z&WVEJy`4yFT>i7R+_MI~#p(-H6P((hU#hJjA!S*Rlt!d+KtJ?Yt(Mr^+riGxRb0Aq +zZLR6I(i;PyIsU+rIdI1@!hWA5jW#oeAz|o83~7`EtL(=I73Ev|$7aB^r|Q_s?oFHl +zNaf)?jL54M#$gx3xWw6WZ{m@sFM(ps5Q1{VrbvL0nF48D6m>#qadY}h3?QkUgGcZ{?Bfp}JBO=FXADf6U4kVhz^MUq +z?c&Ey0HI-3@f(GJNQ&vIN_{iZ +zgKx_2z!K>v<-TKxg$YS824Ggp=<=Q|+s(D4a~B}R=hWTB0=h=0r;FlsExTUt62Je( +zC&<$uE(=Evkpfw9O_E)L10i+-;oB6kyIP?ifkjsZma@K6#*7KVkZ`*XzxN1Eo;;3g +z=_+O^gSRr~VSyoLa0-ayAbAH7;Aq4^_XZ3>+<>-MAXkN_ssS2N7V32s13-{8VdjKD +zBSIdq#UoyP?M*!T@)Do<(4y=jWG+Fgn`%pENkfsLZc0JiqaE;rw*p)51MWYm&r%it +zC$zst{c+X(IRH<+yo)EEeicXN8G#27ClGYkD7MmPsQSKz^|EnwkK8ChWJUB}UnV`1 +z>op>+CA$gX3~;LJu*R&k@Fujo4dFI-IZ#9DNkh+_*rAxRY5O;Q?I= +zn1vBx0bIPc!Z>hAoh>P}8-y3@g8_cjIHLP*?{MFHZ$+jfn0E_wTXXPiff(lKVnpZy +z5N8O8vKx#B1X;q6DbW%?fen&?Fu*c?7Zqn>aYz~90g0uAcwX+{%a5JKDz_!WeAzHR +zIP(bju;56*U%UnQs~-dwGd&+`@Q^o#8YA{WNP`CI{nKx}guVTJbp3#km$G5Xn1D1w +z)EJHQE5$xc1{bDegrY8tlMXh(SNS{P=v6x1%;IUox6Ox>KK0g|Q&as%1W~p2PmP4A +zod0In01hnyNRW>}m}UQ24FF&0z*8`xX!;z><_xB~aga=|>XU7e_WD8(>;GIzey^Ki +z6zMa=l08@fB3nO2{&%`x29S_BiUDdiBF=zIS9kE{-U_!Y7BzDXtu!#v&a1erNOF7| +z_@Vnx;Oj3EIz-u4J47&d07lUnI2I|$!b!6YS5+L@t~$W>Qxqm|fh$z7mNV-u8( +z7nw$I>M;%x*xy0mtN7-VXYl-|?!iayo{KTis`fC7$FvyS&>HhX6eRxi3Hd!jq6Ntj +zi$?s{<;au3pBK*$`0}4Ti!fY8KkSH6A?+ixhW-vCC=Ey$i;8BOqDa+WEuqE|7f{!- +z`ui>SgnT^!gON=Vg7>{Uz^sodggVKS%4xM?vBH_{l4S^PxB=8S0IqQWK1mJ0yJL4? +zpVMEg)YiCmeAMIxsmaELSkz>ODVl6J?M_t@YAQ-jI3GhRth#6I0)VOf;Elb;F$?!* +zwOR+sHtUEmjw_tLd<`L>`s;bxs?AL+{+GIX3HZs6-GMvrJdSbu7-Dx4^VtGnu?3oM +zqw5yvx*6iUL+Iv+G(!lT?6N~MM3M|bbP+_IxDcbp5`<2oG}V(}V>aVwF^*|TF-ikC +zuaMJ#-OJbT$YZY~lM4;bo239-6SHqva1e>GQuY +z1;z>(8I(p?j=xY2r`IB(~2b_81vW5k(Tgx{Qf#Nn(k(mIz|MnUF^&fc< +zn4iG*v0Jb>dJE>;$1&SJDy6ixwh`wG%w}_R^DT7qIl9?gcj4JWoXyd7bHr|r(9IE| +z5Qz}&%S%!K03ZNKL_t)e#u8Y?*%~e^F%B9=o27Oh@d!#ILSA7z_W0T}=Wy<7uDb~F +zW|v?i2{=Sw`-CaMt^}a$^drn0=dWda`8zM;$UGq;A#y?tg6zbW^1EKWw}||JBM7?g +z8A{Z-t}|1036k|_4Xh?-4psxm4X}aMDL__ZK@snnS;Ni62wkc|>2-31;bzqcJbWY| +zB$`QnXi|fUIJMA$!~>+1YrcJqNJhnI^Mu~m`wvnJ>RA)NU?MS3?qd@LP3-qkVJ0@J +zjyWdeG$4%w#?=xpoW6ina>(zb0Ix~{75Ga7K_IJ)_9 +z#Q71>Yys-F5#j>WZGmQUP`5ycTcB<(pST5zbA(Q|-U{6u6lb8&RW|JAiu`!9dIGX! +zB=XP$!ybS=oVjorUwiH{VstS96C&4)Z6f7Z{8F4;YsCNTBL8yD`TxojXL07W*TC49 +zt*}--a$bUY1ZUaIfO(WrlubQM63AuEiYLHyeM1Wdx&xRO+Y`8gNAV)!}TNi}C@fY5YUww1H`Llb7b0DOB@CpD1 +zqzL3BK|Balf_NwgP?rG&@IW98qSG^kqeF5T7(n2Wz$@9Vq*= +z^4-dS<&wK%g4Vms6onSCmd)st24pV$Q)eWvmoA_}_eK2@};0BS7E8R!ShL&Dk1Z{pSS +zd!XR-WK&N!tdFSy7C%3Wz*dyzKXg}zU;DQ{fKPnvL5xRkLq2jRW?OgS$dO|RCr=_Q +z?!eaeZP-3`9Pz|Sbc>`HA{s9ixCbNIThtb@I`$yngFT8>0&c25284&Y; +zhyg&-%}}kj9~Tb|kzcEfQptz8S!4OG?mez*Lxq&=2GPRJ!TL6>-|H`JxOeT0>HCCI +zq%`HUhTx?32(tyeD~f^wfNiK@Im~d>G?WcDfuZlq-sZB&;ilH?WPojt<@J91%gyc8 +zPukpHPE|=^Gl(*(VbIm!138^i{sv|M2_a@gVvPGezWc&i{J?_`HRZGti{~(B7wL>D +zd_;(Z(20>B0I(c^_ubavzxerw@Q2UbjxYY{1-$ayISfl6bt__=h +z3_~1%IL9nR9Gf4*hu(JzyI1$|;?qwdVhb?=IV6x)F_ak)sX?*`)Dr^&@>uo{pkaZ$ +zzl*OtegU6+=qP5qKE6%y{MRD_MZjj~_xBP217H63ONgrh9ePk05JpCq86e#{OG|$1 +zp5C&2Kq1;zSBKW#%P9&LL^amUbG;wIvK7sywnly5 +z;c@tS02-fcM;Zonvl+Iww$S%IfJ)(Pd+=fCG2faa#t!S#sz^T}Br;u9F<4W}c>S8M +z%vv+IsQeb6xH{yK!S+385Ge&t0SHOd0s?pp0Ix8Hh`j1BBCr{`_C*t$Qg_cwE>%r58E6Gkbnh&ef-~zj^=uJ +zuGa;yO;ur_SWzj;2DE)&Lw+ralUdYd+g6Wr1}i>U2>xM@!`lGZqLr#)VjM?=7!foe +zsN^<}W03-wvt~gF{tKV7YA$H>u&Oz!eZan2Cnoj)au1PftLkbh?s1hWx1Yz^Dh!$4}62&mz|0a?D6 +zKrePMVs1eH#>tz@1~6Q;J_kU>_S8u2Z8Rz1uT2kUx7tAT{Ru)_E3=0DN^)IJ)uRSf +z8=G|h=4=ddQ<8x!3(LDF?7Jm1bX`}d*wmLbj4!C1P`4kKn1BIb +zDY6mzggGto`uX#C +zBy2b<)3u1(!yJbX1@1az4O>jQ2z8^wO1%KojX-p8A!u@tYp=%UJK?Gjb#B+Exh}w5 +z=!Fp?QrIAX$aH>vlVo_ne8B{$Z7hJHs(`wbGclMgq-{WALJEvA6GGU+tBDAAR23jfkeRZMJK-feniS6 +zavDW{CIz4*95rRuU5K)1Vu687B30xOV;+IHg&Z?}|7%a;r+?&L9GQ`12x}RPbQMF|kP +zwaPzblN7sT@WyDfL}BWsH$r>K;(GLsH{PJo*_>c7VwBL{zV8u;ur;5f@B1P}gGh?L +zh>k1a!XR~n-XPVz&T)7Nz%~FyO-?X9pY^-xELrpGEymsmh6T18!cXC7O1OQoP$KB2 +zo-Wppo&BJtE`XCf%s|v~2(}JsPJ)#O%VKom>VKv*fO!B93Aa$f*T3^E{Ndwg5F@ZZ +z0xSJkLXFR@->4E$4WKb;U>;Sh??f`#>Nb3$+&Ak)jR*ladUOl3umz+MiKN^(kExW< +z8pjMC&7d|)PJJ3Mrj-ET{t9`yi;N{WB^=8Go`3lTeB<;*u~YbpL5jqsHo}IFtjomj +zpQN3x1HSUTm+{i8XD}ZJgmDkx9$=|fB$b@ATX);lHv?MYjJ-k7=2Y!C&A~1dnx{SV +zr(1+CfcUlr0jEAVHB`)ARAken2%i*R2!dNlD`!~@KnzjhIv6zo?-0jL$px@7;ElE4 +z88lLQ31{LBk5s*|l_2WJG6VxG +zuRMV`BXW{mc976v#Q*Z!U%?A!mp~9Au+-0Lvg?jn!8Z{Cixl*G@Xg#``*hQf5fA{! +z7BkGZW{AOd1tP5~WD<#tHCWjz3#(<@^;q=oNC^=G<`_Za5?}iEbLf?G_o)&B8<%$z +z`i~I{zst{%rQZLwec+2v|jJLj^DpYBC(fv{ocW$`Ttph;}}4{T9wT~WjnA`!{+q}T6VAN4)4W3 +zGy$-IdG244H`GEPL}=0(i7<@gItG}S?`F4fbN{>51>X8tv&8ZG-$u@!=$Bq<&~Vh8 +zMqnI3c@INgVm1!Ac;+?y=Kt|kT;3fq0@xdr0O+S6O>ziw9Ydi3E}>@s8e9bjXU$ME +zaAYw<>=-%9#+)^nN%agHU)Be#s7BUY2eom~Dj}qOjCqM!$oS@WU%+#(?IV*AHM4gN +z3F4*5bqnw2qe$P6)jqNs0gAx4pS^%*o_+;Kc!iYrF{S}Dj4A~TQX{NzceAY4y#UHO +zP1n*H{6yvRz*MEk0|3S?!~wV5z;W=S(iy8bZ56Z3vTvER=2bI;ddd>n3A#CD!4FMB +zlGgjTjtA$T^y!MD7B=j(rn!Y*0#sf9`kxoQ!PHRmqQ3(u)64E+qpq{I~yGkH7iLpT})S +zJM0c>_=>VMcZ$NTL#_!jrS3jEpqWC?M7=auT1pd+n`Ip$5ypOn1g+4Nta?rc5=k6= +zRNF?5YUhB43nz>mkOPB4CpGxufXn+MzWDX$@WG$|7?xx0Vqy&u&aUfjEf{U3Uz3bd +z0Dk`~-@@LNiS*C+`+&r5f8GeSu6ztIgMhU$wml_P`llJOuJXffJ;n#waRN5(}yArrmWC +zSeM5U91rgXyeW}@qW{;~Xf_BTi9WMfX4Z+3XgdjpScCHd;pPVP!JLB1g_<)^1AbIA +zlUJg3$5bPNY5h$otO1c4Tu6;YmVs3Rg;}_dS?B@*c?2avDjs&RO?s^wjOee8z^Vs$1r9y(NCAX`MBNHdID8K@YVA?sVP +zu-No;ZS{PE$9i9ZmbWQrmM6(A(uM{YD3*L*YmsJAk>KCFO1 +zcHRJp05%DPGN`1!2+$FN>dYg+5i|}!zXakAjxM(Fk90HO55E3A?CoB~ +zd^>}456}SSjG&2GX1S-z6}04+ans4i#!${V@Bnn@Ak=$JzjyG`JkS`Hc=vBUxR;s&zO;UJN0m`>Cr +z0&s&CwI*pb&(iv1j150bJfi|IRjSA}LWU)1Nu&^UVuQdzA)3WZl@Yts?RuK2A-_+f9aJA*Cau7; +zMOZB6Nc|ENMRZ0^wZd!$7+F1C3~kyIv@dGe)#M7iH|>k +zqjLbFLC9ljp)2@Qr&rW>lJMi%40!e2fPenS-vVI|X?Gvo?}O74NCOxNlrt>-*N~nq +z&DVaH@A?v}^wih};5vbFu7ZW<4AW}|!O_9TCYUTlr(6|aOQRyYdd_SIU~f}1={cw> +zV4jR(L_frF6GDNNX{aY4C5u+7+}A1tSt3KduACXJ)VDv3;CkbY{UyM3xgy4~v-oqJ +za(Rix`a3QUfRRL}_gOpY7nk3u#1WJ>00a(T*4T$Ep|s3`1xyUeD~!C4oR^4L;45GK +z=lG-VJ%Jzlp&!K;e*9m;M;^QbC$z7md}vE-x#NcJs=z_c4JJrb+IQgi5q00?2@z>?>XH6h4!C+QE7^buQKLYf1}GsFyZ7}3uGj9ZxJ1dbzCfk5L77)OM8!edXrj0H%+yAxwR<*jg(GPLlgsQ;uk1t~km=VL#J|z+%iV>rm&1Anj09 +z0k-)i1%NY^vR)hj=1>D?R3fVq6sZcpQpr~uc4GZHwQPuoiH?OhF(kl*belr)W;8}4 +zyDw{*=1z=O;0TbE+Lo=V25fIdEO+0)KmBJ9SpqW<7>poBNhBnQB!`s2VL%#zm^xtC$N%$N{})!_DB@~?c^oku +zV~oQ|ES8Mz`88ZU9+7u$#ffnV-rmLDVvfcBQOxrmM(m@{Bf6A9<1TXCmmCMq2$W&@ +zaltlhQRF4~8u_`DZZB>?WMtFh70FAmj{}vmPj9l+F(@y0@B$Z8b0As$tN^w8ne +zRe@36-5Clq2BB&%v#7smHDC!Bj!NWkQxbuIn_&cGE`>OL@zc=PHtoV)xc&R@8Soi{Gx!nKRIy1T+~Weh&YapcQ{nL|cZv-Hrz-1dSmaM-#NuG-_Rx+X@x33v^nB(wnK-EnM +zqW9arb}0=2s;|;^47RucDRHO|XB;kh%F&PF9hBdVMlk>|BWl_=&|O +zW<*H0>&~RA0K|o;@5!7&qevQ=7~p`+gq$J>5l8`?W+3V@>K0&I7%;A0!)w$!2t$uCFTsHkQotf4P?taqEJ6n75qa#v3RnoD +zCv(#H1$C9N#z>bi*_JF&Vi!L?dz%q$bqLzJlc9^S3yRy30@XV8C|sB@`8)5IFeX +z6IgNQJ)9MgDUG(&!QZwSu3D&UiBcs4QFAoRLq_DP|5!LM#MWjif$ShWzB_UOEX$7p +zgh*fx7>1#AF1eUyPD#swiGJNiMm2Z~zd)A%0jyKdO-~v?msK*+nT`9qYwR~6F{gia +zxof=JBB10&=WGD(^2FeYRgk%A5*B~eWjsl6Br +zmNIA@0nQpcL`o>K$VFfvL-yuNB;~CWA~;3Fz#7mrLzf5?2Fdapw*Pmf9m+ly>ER?pKUnJ +z8gQn37 +znIJ~C1kt65q;4Mrky!HXdBlumjG2LuBM2ixQrFioAcP(ifiRBfQo@Y)F?57H%z$A9 +zMla({5RM+C1|UWtaE}~Ea1P?m!yqvlnWXZs#Y|+4u81PF$Xq6{3_?^IGuyMwiCE$Z +zz$krXM&=nPF+v`ZqfAvqK){GD6FBz>8nX{(jH?02J#tLQAT9DULei{BtePZh0G0JL +zj$h@(VgoW2z^u`Z+gF`mVS#JDNwrP^4TLCClSzL>(ht2>g?g$m2SZo@X3ugB5`@Q; +z9&v@tp&v^aef9mTBE}N%(;z#MBv`vI +zATUMUMw==>Y|$OD@HePIuMzc{<||;xBe^ap6fw%XOf1a$ItFrpC8`*p5ND7gH>Cgy +zNeBzGpp{!j&H0hX}$ZL|69hpEWSvY}kXaHbI$e~f=*ICp-L14{3%vt)! +zjKm{&tTq&pDlA%0`V~z4F2+4K|FCpszodHJ)`rno(OYgYfdvN>g>9%k7M<0e!EmaV>RXW;AxVgtw)@M8%cwtmyaSIM8WaxN;!P_p!C0wFcen3bG@0C65IEjL#? +zt8MFLYsRVCfZX^>&6s4z1a{zC?TBe2=_Woo;080Sek_6`^c +zIp~NpK|v!BnI*u-^!?a%#dvP!h!A4g5X8iL^Y>e#knJHTAbWTcaC1U|RgAH1r>t9L<51+1)@a5+5vi*c +zvk{Aojhn9>*hp>HmVi8LaB-__JW81$YrJU~m6L=DWXjFUePYEc-C*`0N8lVvDKXAe +zWXvKqCIRMLEW;Y(4HhPeq(Mzwba({KKQQB%iX5SELKi4gI3()tCgiOmYdMz*-u6Lu +z-9KiccD7s929)c(7b4>OV`41Ky+=A98okFxu4?}Ut}4>Auj=Euy)yvTAb=H}29DGebw9LMi!y8bsxddUm#~K1N +zP%`CM{QE@-G$;s)Al4&6hOBl437CQ)HwP5!!&+aAZ5ScU;3Q&V25Gb%cY9s{03ZNK +zL_t(r23rVkh9DMIkt8WaJt)3?(iu|b;4?O% +z#7l^z5)-51Yp79xT5aNUOFQ)XY#n?-mK@jgVfCFK~py +z6Ue>Cpq0w41sY0IvJ*5H)!En4G`+uMhmxsR;&KiLa<>~l4ov`Rb`ombf67SNwl^Nd +zM-D4;ARU#X7A-fALB)JiA12~h4)*fec4LsK$N|?kD98r&l+-@j +z7(7uokw26qpsYwK9*8PE)IOVVZH;)$wyWi>weQoz!3LM#8{i6P;m8ejBU+lC%ly|+U`x#u&PW?} +z2BL|gW>;9RSq%VoS4{V8xj@r&^P48ujQiK(B92>;*&&o;sSlSlhCOROfgx7$DF;@U +zAr)eiNf0GL^6&B}T2g-He38P8m>1S+Kj09a%ZJtL(tyqZxKl%J+wY#d +z)G>M~sm@{pQpS7=40_j{Y?4#9{~pJJ{D#~TC7A2}8aki0Imls;n`8t8EDyk^{g(Uy +z_c4km60cQKX&vO1Hs<7wa8jW+xJ3N}%Kh*KbBmZS*cNZl~Du9dE^j7M8@GzAtN2fMF~7vO^6u8-v(Mq{7wYsQn`ex{4f1!Y`rRm&sP{A!Pu +z>~Qk{uia@kb9$^b$U_e7)rD{oZL;!k?oj=oR +zoyD$09>pC*Yc2Z59l#u*^K5n)x;$d +z7-Zseq5AUfB9$mGl!Dq8Tkxun>BaovN`m9fn6^gI4*i?@?@5RGWjSB6WDB%!0fD%AUO7flA +zv^ZrQGS&ga+jtCdP4#kZvtLIpwx+U1N_{uN)kH}3&3f)%&ByLy(LdTTjcL^4IMhhU +zMaO5xIP|d6V4dQV*$fVQ9KIx2L1?4WRoo9D)XfZuw76kLj8YM37>5J7dOZjTrB3Go +z_g5Zi5n!#-&JZ-d(#Tm`BBT +zMlSP8WlpOIRJd_DOI3|z_W|j6sm$AEc2EgCD{NM2Vycu)zo8mLNL6fhZA5rr=?Y!`lF~(`5i^^e}zZTo#BCS?>`qK!mw%ys5_k +zh~1)Ac&OA|V2az7F)`2~DL3CrryVD#&%)ZYGt~Yr@8c!f^`=d8Jom$cAD +z!9JBJiYj8ya(#QFo;cpCgt2+oCSq3pOn>_+oVo$s;cNiR^|ybaZ)?+gc+yt0bk=TI +zC%~lX^SxNd +zv=nAqkC9jBHw^K?NN-g7}y;!q6%?ReMvzTsG77*K?zM#@-!PXB4d_z4r3z{>Rx +z7b}pm;D$0ha=U4dnh9u3HERM;2%xDffT8m~Z3^mpx4HKfj= +zpDThr>s)>8em93*NAdO7aZc8#=p7yc9O7G8u@O*%g$Z*gZJDdKG}|nC^BzkNr}pKJ +zSxlkQ;K^U73Gt{^urf;)X40A(mg)R0ui7V +zOi>Gh+Y_Jy0TIv-z5J-PI4d>Ct=2S^*iEx4!kW_oiwVbZb9M_}8oaLkflEI{tq^IV +z`0<yJN)1%~zeXT*@nyVKkWLk-jBf6~Dcxw%IRKp{Dr< +z{v=I)TkAZXXI%?_zYvTn)kqVh3~5R|RNrN4HD`;Y6?d?3f2jvBj_KV|5vcA6 +zL}f5G)RZ$q0CJ|n(ak%+%;@Ig1+elO5|pMB&Fo;kx|rNFq3V^^o0{li7plZ=K}kSy +z?G$9tdz-ejg>sD**X)J{tLlB;hEVtnmpGhC2uS-I>OG~KHr~p`Od)bK4NZ_w884%m +zB!ZPNDEg&068d?9f6T^8fTR?wZ)1neXnXV7OAS<9eX`Jxcgrmy>wM5{2RvI4 +zX47hJIy{#Ks!NKRTPk>I`ft$tITR$ao5={wTlH}bdGussapw96LtSi=-|v0uO@9wM +zCM?{wzSgx$!ZwUSk;^RoZL2_44<>}EU|7NG^*;JnPVYK!4XtghKF$7FgTN_id5dGx +zk8+x8iFa;pCm;lbAbB$u_FIzXaz==u>=evEVWM3ZZwm4QHzOAyjk;SYz-ldSbQE%2 +z!cj*f9NXJRu(!LfA+$AY*M1j{i4Dt9LP8yf&<85VFpgpkP{kW-mrHXS-XZ@qc?76< +zRG)RRre?|aO?Xb8Q8ojt<*w@>*QC>mVJ?Ws5ZsM7iP5R!TqE32jtEl0i+|Xin4Qcl}yMZ;LdA4Yd6* +zJYR!JClLD31F?+>bF_X0h&N|@u?+%a7^N~4!&Cy4%s?+O)F|5;Z*$n=rg;JSzDLg0 +z0BG4vMi8V}NTp)KFd)Q;6UUF^{Dq5{0hRg_6dMnq`qK`ST!T%Wt9b}jls@2SEd100 +z(&h)+uZo=Ygh3M(x+6_H>e0N!u@UhbmHBP*5bQPfY~idM!eTf5^&D^V{zgsr5qf^0 +z(_|oE!(1`0H+OZte7KAO`g9DNDD$+ndP8IUJMbtJ8FdWT6aS{LczxKcghTIj5}f3S +zFisplf#51;MVCKg-ZDTC-)wEkO1p!b{W#z0aaaPt%;=eTJ`6+MA3)QB0TjQ#-|56$ +z9w7wWamSr__PJ-ZF)0m2ut+HPBK>z0Z1|HcAbNnR04q#)BJvx0H9kW)_nkY*zP0iK +z6?3T~wnCyMqZpT|O$pMj*)7U-muEokyO5S9i+woT7Q*?Y(X5Y;7bp8*V +z=S>F}3RC8a94|ef?dkfqcGdOuHObSYwC4V%u_}jG+BUJZl=i2v(RR3*8xK`z8m;6LEhd+sikfimYZ`R10ZR8+oKaa8{>CfD2B>hBHDqJ35q +z%?hG=H{i4jw03gZE*E4Ix1V?2sk^RNi_J}z>w6pQ;_@la+G&5kZ?};ySo^%G?a5Ij +zsx~5WI;GQ-^j0@fy=miekmtbe8n;b0uV9`2?4>V}jTg!(XtVB5c{CkFqvb`&?X(B?YgxONvab +zZIH`5D&YDt5eO5*(sVvtP66q&UTmhxL^}t|w84$jvXOcFU(*D9H;zV+-|#w`{@ZZ= +zx{mFT9TC)b(_~H#?sL7*?lj4l8HlmN`|p1MecxAxSd8i7aS6p^mWzRXHk)C&FWabb +z<`r6eeA`6e?MeXtMGSyb9#_naot+(kC97##-K0j?pllnSbH&^s9Y=kv!_QVKQ;1A76hlSvjr;D3tFl +zQq|K<9{RNXO+>>XwE<}bX3NU+JJ)Jo>)U{4)gR`9Q>S>jVqjS!y>F{G-cl1`c90h?EBHo?}lgL-)nuc +zrs}$tcFK^ZM5BFESt2E>lE@>WyQ|d_@4x?koH%(4u3fw4)a2Y40cSIH5h_!i&1Rq& +z@aCIu0szuDyonMP+N9LnFcCP61dsuwG!B;m;KCahWguD)5RlT}MCP_grG}YR3m%3Z +ztJMl0|M(}Rj5ts@Lm&X51`yR_lWy8*W$SvLd$#>GwJ-Q*s|#`MFiuJCd%4kn-+(xi +zI}ZA7k2ll@P#b_=FA=}2G;lJ$a?L-x4ja=EG&8els0YonyXN)j+Q9m=_9pVrx_{dD +zwiDC_^iJAdTa)_LDGi&SUF&We3Ow!3Mj%nCSL|RArlwmy=W)a*KKWx9(;x*u%6=LV +z48}NWRqf2B0H`t75CSe;z6>zXulmbSMvOmW34kZxjpC%$a{mkfoI7_OF+|}gtg!$D +z^?x#mLP;H_Sfzn;=g#3rKK==uIDP_2D+uST<-$WKx=(lu!%X3n4saY~l?60{(g5>1 +zJfrHnw)mTJ6V?%<{;shA%{?Bejd?X<^^}7MVz;2KNZoW;m!*|GH}qD)+0?S40=Tvs +zeDMU=$GKadrli9LJ88(>V6U7aCfu{@s7=L<4C@1Q4odF-0k3uhqrqVRF51}JiDoo` +zE2_QpGf?JN;THfn%N~b0C7d{Z9H02ar*ZD=`I6UfD;69|dP&Iwf8?C$K~ +z&bvGztoHXn1mv8+Nu4J_-Dofyb}K4YK}BDfgNRfFc5;yuY;OIDhSvvjMo@8?Ui{kNaKvQeRd9ZNm_nx!Q2ei}iMwHRK5! +zmlU=UAF0TVD()trJ2R%lKmP?-#h6{Xu{D=q +zB6|ixegiyt_I!|90KK7MZ@8(nFxgro$J5qI(;+nk@wQ{Vj+8*n(&@rA$}rO +zctUsmW4)7aHDrH()3b`_wN;)z@ix0;r^tr34&O4?gJ#G#Sg+IM`a$qh&To{gcx{{| +z*eyhfkOd=UVbVEeEVkzO*)RM&UVrU1480`7F*7=2oN5U7(SH^u9J`1M=g(spdN6pr +zcIDDb5ajo2zzrim@XjQFtS=h@teEq1wcLAwh;ZS;MaclZ8b61fK<*qf6ejiS9j=&hb@B^+h>-J!3#0btqkj7Dpf~Zpa(7qDuKuL5imL8PaU9A^7{&(OC}m +zkT*GaI(|Z#mV@-#>DxUa*w}#spKKAdZDxasZlgf8dA_aPa-N$%?V}r~$Dj1bex0Fh +zxNtDRoveBA1WGbIGbiNK0}KqS9=F|k8-DTM{bjuT^2<^M7%2LEN~x3x_v%maq>$jy +z3opC?0NCH#eG%XX0AJzkc2N57ob)- +z@fQBqUwaKljvmFo_e;NwezgYzfm1>ndMPPZ?yaGuvLmmz0NA9Je>UjATt=~RwkCQW +z`)66*Wu_xK(Xu|qZ9g_Bj#G(pD$q7!XX84X_L1FF)A6i}#Laj%xZUh`F5{eZu9??r +zH)%v+a2;IfGwc6yF6+l!Z5t**U$yQsG*{o+w5}b#^L1s)`23o!YY3Aj>_u&Vvdy6@ +zWI-g^o;hcvp%?B?K)=6-U;5>LAIFX#$Jw*z0G9avQtd}w;}$w^e+6%{1OP2pxOnj* +zQcAdT`Qmp0>_c$SB!IU~1m3O}z!QNHLVcE3-@Np~n8wS@j2B*b0g(b`UDp`?VvOi! +zGnpuxz?_RsfB`)I_+$9#&;M)q+~FW|<(On~2u +zwdE!%!(G4EQU4p@ly=*}I_l=`-q1?A8#aZ_dKsH0y(s!L0ZqXI-}q#Fg$6XQr^)Xk +zh-~cclwlt{h0_-5TKk^f;1aZLP6u+GyVb1X-llwC#e*vLD?&3Pjf0l(irBew8Gr7l +z{yhHGzwmiH`Q(#Wt(E{#IDb%4-`mJawjVQQU5Bl$Exh>R3$ppnFkHTJ`Qr1a62K-; +zzzq?BcNz#}1fYjdq21-)&ZEqXC!Tl$L^Ya;Ay&|F7$wS&+pUgG0?0Yz>XoZ_=9y>l +z*Z<>R!3RF@0qpEtRgc1eJPrV}7*|3-h`|vA=hi9<<9cojg{pu1k3U+K&$+ssTW`|~ +zS`}adAYNAWFSPd*K&q$h{cY#k=ZZXQ&o&~l?~#AB+ELi|hfrN?wNq>3EjAr}cTw>- +zJZtunDtEUcdDB-fnosAsjHim()Ah4HZC<0gGIg$;>u;@F?f7J_Q2Jr>R}h~fLX2f+ +zUbQric;bmC +zG=T2fqX2f%N&x@D0)a{l08|11uy_9TSHDU`yt})LS6_P#F-FX09SB+)EKuF$#38+o +zUXeQD)mLA|wQD>0uYUE{aO$o*vAcT}6r`eX8ha4~LjZ+N+$?1gLIJ|-vj)N@OZ)ow +zx8m0~dQ>T54Sx^nqI>qOt{<*n{_8}H-#jUDtSR&Bb9KF*O~)>;cMV>5Sev8*CvRK_ +z1&SocZ)0_xKJJZ?26G_eh&bs#z!_NO!l@f(KSMyjnvuo_gvj#ID1y{Wrgk_uO?Cc6KTe82c4i +zeX7OUs+)`wAmvvYHr`rzH>$My%XCt2Hz*1NZvq)=kY~+ZRDjK1!I+Aj?7wWnxry{L +zeZ{6{Tf2q5c9Scy_GhD)tMs`(KYec*Qe$J`ia1T1`mXX9jq0)Xdc#=17WS^G4Z*iu +z)`XiiDR!LFzdFUuH|KI;ca2NHk-(jZ{DMMhOc?tWavBjs#O}4Lc+aW3@V9>LZzIMI +z-}~P8icW7Tv2lOfKf{o}Ap#*{)^+&KqmP0aSnlsWwZFG>22}zuJbc?80N|aa1A8f; +zhftth0M{;@J@X|Z!W(bAf%6wHU_KLI{ +z{yV>c2k*Zh*REU=WM>vCJN7C~NS*=3C{h7+pcsowC5EUAWglHUFEs?A=z%2ghys;s +z4YjvElByx)9`{#07Ss0x3P9;g(D%50gz(nCY5fz73#~o7KHgwsY{XY8UKXqDY|l!c +z72r+H@^ce>TT?d}xiL=P87A`_*1!AtDfh+$+G^Lc`D}caHuTpxdenIe1-62&TZj=M +zcJfhupP7+{9%)>uiW+eB$|XGT!2S63zw;Y7dCM($?6Jq1U4NMwV?PxCzcIjR>U;J&cz`Jch6UtzgiN<3yaFT9I6+%GYFEO8Q +z;UgdU2u_|jf#3Yi{~lld@|Q7N9K&KBk(fbY#Hoo}9-^c5( +zzm9&j|NM*3JpSJSI1S(ofJ*>&PziyL1*lMXJsf`r1b`p}paU=ma0I|f0Cxen_vEd2 +zeEOdEJ@g+E5kB>)AHzcreGvPr6_)$^TH~xOBszT;t9wNJq64@OqRF!pRjtxGa__0vOvP#lpqr~))E;vKXCSj=ts(;y!6{>Z?;1{?Jb_>QxBngd +z+|U04UU=a}Jp1gk$jqhGmpAm4Extl1*XuQ@1a$1^Q9Sd^)A-}B{V~A6Yp*>2cduN& +z_y~Yk0h|YL1)_p)as`&-W)lEFmbybL1>6qc9su{f@4*lMm7~W`{_tY4z|a2d7Z75^ +zYPCYvEWfPWr1;gPo7P)Ivkfqp>cV&4c_)702YwKj-gpCl?|=QD@bDi!j8#8id;6#w +zRFva;F% +zBM+B<-8k&Ajfh#@X{8qv2_Tny<-G-+S$f555@UoGEd32w!%p`FY*W4XVJt=SBp{`9BuOMm4*z-_nPfybYC92d`@ +zFPr3;s!O46@jVUp2{B3!_57^s5JSLk|IY7VwOV3t_u8YcU6dDf000=}NklKYkFm42huFeZy`~`+4y?Ucdte1+M=V?=BD*)(v!()>Sg#F) +zB6)=Mx$^VuCU8wEgBd$LBC?LR_L&Gp1@Pmw?%OM-D!^0%sFOs6Xo*Qk!40VK{C +zda*$rJ#qv;`lBDmFZ}#p!bd*(QJg+=1}{DTBCe_X8bFEcGu?l>#dle2W))lmx><*< +z`5fQ=*0=DzC!RoNUcUU?6TjZCmQSN{|Em!0Z*c$z<^ud#5r9p$fLj5)2f)3j?s@;u +z-gf7E{vrtY+-Eof +z3Tsnh(rodwiRpR*AefAzwkVNO8dJf!AzCPEizfh$T(?H>oQOZ|ke7IObCSS?QOhz`ZT`sm9Kz8z~zf)fA8Fxmwy+6{AU4N#=*M(pE&`rX|v6Iye;53 +zfIA?nz`gf>;G=(SYx~IeEfx!W{_~&5(WA$(zg*Ty05mBGWil-GnIOSFUR<(!%rzIDcg}S3MZDLNR_e?s2~$(pza~y$0(h( +z^+1_@L6$Yd+%!HXLa@y<4A~616gqW0B?n$l%6=(MdYH^>=DKEO{AG%`ncuR^4&`aR +zh+k_V)JhkAC-eFpMMicCS7C%JWbDj}YX4 +z4ZuZ+7r^WO*YyJYSrLG#6kw{rtq@hkP-ng5K7UI;wqWXap#?P;?~=4!yUKZj>Teu{r!CaQaha> +z6^CP-A?Ji~9GVyfTQDN2!eR?&hrCOrl$>P~)NJ!j5|yX`X62h>3`KvCv_b +z5*IIC#O2GEap~ek?5$R^gHNbCVp)`HN=Zt7`<#9V!!u*Jg#8LSbzO(WVu73!{@Itl +zgo_t1VjPF7FF*UlZwy2KJOueKK)An&{%^_sZH*2(4nqL!uh{~os=z4#?>%wLZ9j7A +zy$}3y*LB^!_uh-2_{?X(%-GvomIbvWAukqL;ZScVG4;UjGcX-w*L65@IF1^|35*JWL=iF8Oh+OhB}UZfFl#@J +zBVy=~Shzu_?X`2vDukFnScG#ykf;db_lB#;DB}z0x}beCH|yx!O>l$@=(<^{NR!49 +zGo7cRj)kE63g&|N1{a)}i-)Z&TidyFYze}5l+-=pvQ +zV$e%tj}WTC+K8qRLX#f5E|$$aM-3EWpb!rLSS%Kp%{u&lU;YYSIsFQlc|3RKrN4Le +z%{RUQarvFYy6Eq@KkzS*0031A@I=6?0=Gkn!0mV4{h2#X-TQA65q#~a#eM!s88x%Vl@tG_MqlPC#@+6pa8IO&H_Zt$m3W;FUcXPzL7!zr-ZG=0(~#r +zGldwD(ty~_i@0V-b_?dMxX`wcGrHMaL!R>*q8MOJfu+fbaPbmGw5AAeM~l?;zQXm1 +z3g@;t^TAi-p0zo$2sH~!4H{+1TG4k(g!{g)2HTv4kZNC|*0Q(uy(;J1@MG}fF!g9u +zq8z(U6T>Q_@H#cwx1!~qYz=1 +zd=*AU8pcY@Ap{K!-QB0I;nvm`Vhs4kH@=DIpMMrPWxjCs^#6V7!r8B)BL5p$7yWN2 +z1Mqq{ewdwse5*~75LAND0hqsW{`E&f=uX`6o_l`!>8GDYN(rC*IFSSh+ueE>SS +zq3oP$b^&C7oxxDJp@_0pToFnj4Eao)3+_CC5)qf2ngIYH5`v=xEJjFX%(u2gtWA}$ +z7+?kEfJ9L7zYD_&0*X3>o1~B#mx~+NH +zY8pV%^$qzAxUJ|!SzI-^4t +zyj9ELESqH*=d?^ZxVGi}49+5z*cy4lX6xZqhd_j3kadN6qwF4;r;HFga7qPUNY~ru +z-Rv5+r)ALhnhVi9T!xS_MjSnQ6gek+^Bdp9(@#B#7$YuUJolwb7tTHc5&7Rh_4j*= +z|2rxHHa!l#H=yf)5rF|BqIe<@E}T96rP#&!t+(Cvx#ypM9(~{A(?9;>*j_BKRL{Vu +z9NV@_t|mBrks>QljfQ}CwTL^hh<_9A+I3xHQ}7&G^unw_W*nGBA1->g6e{uivRmzT +zJEl7z7ekK&JZ7oRJLX(~*>q_jH$=zUF4Bz$V#i%$&5p&(VG)NHK +zUi2|y9EK`!xdoRotAycv+_trq*-EV4Ro`P4BZ5k4({NlL{_5A->(BO=0E|lLjgSIp +zw$?6dt^^~egoKF{IC)o4;>sLKyPnVjq&rxdv6PXsS?;R@V}!v}7cVKAL_z@OTU(fS +zVjFqn;fL|^|KHxVHAit=;jb^VD9PkAwOt1w=76^eZyL0J0bbmc(S|eOUU@2x!)zs|H?%cZj+`e=A^wq0y +z&f)gT(qERY|L4!}%)boJ{4(wLf2#bm6Ya(Yh=0R&SAclH1blwL419QSc4qeJUmrd) +z_l^S8^z<~|eCsV7oSZ~nl-SzZg6;KC<~dj_GxZ^**(l9qewG7Ens4cEd7j6lj99H$ +zfP4(yltCy_!ct?_0V>V^2*!nTV##RK1CN5! +zRX(8#N=0p-E<8!Vt`heokjA1e0Aibv8fLfHh;|bcG$NOyZA?+~1BmTdw@Y*E$>rs_ +zmL72%meHz&aFH&LoMSIuBEBQ%n6QnRutQ}-%aYI6cpo4Nzi0(G=a=>_%57K;Cj#}y +z`nbEchWFol54TrW;hb};-`#k3>H4*I;X{2l;1T~dGzGu|7yo!v7=Ua8?im$Gg}@=y +zn855{=BYPkW{>_x8)J=9c;n4CF@N+ZTa$hoxnx2hb`AR}X@V%sOX~bZa&rTne +zD1B8`5yXoOCtBF>Dc6axGsqtw<&z0?$+Pqtfh6I4BOljYqJ+D2*ZW8U%E +z14t?EpC-MeR*-Bv4xzFvyp?s_LI`sySX1G{@x=C` +zt0v8Tsf1FD#w#gI1{_)oG^$+0vefs>V +z_D{`!V6O$h^DqArU;OJII|+dvfCH%K3otrxWO8cy=d<(2ey5E&sI|tz$&+~H^l8{E +zL!K8XOFz+~kslCz$GCSchOA_MwH2mjT69EaWJ%xCg@MF13H&++Rh_3Tv|v?L&_<&w +ztA_b1nP2+k+rx_Wz#!vBUq1ZTM3b~O8|g!`ZzRf}_8Bn@dA>BO`94{ec~f5kSs@yc +zl4Kw`@adlPdcF9J^xfro4n86hM=sqQU9TA-Eek0m+AO>jD0M+d(0@>ql#hEj|z=;zJC|!l&)-amVZ7)S;Cm3jqZpP4q*%mu3fIHFD +z75+IOOwu4=BqGmZCVo(R!@CUHF8X=_1y2X2v<^}e7@%orQ53#Rbc|g1PVjx9Rh0F- +zz7HjpU`_+CNi&n*CHgJ<*1;#ONQO_JPr5j=tq9gN!?mpQP+M6I?JwRz3dR(jmzI+h +zlv1d(-}&7iGuzz8_&z}pR-uoolAx=J0*cp(pd|%{cozA)d`7lM6jA|L%&bMP--9s* +z7cXAKxsN`IrND-p8^CMFGGU&PSt|T~&ti;tj1DGzf(PJ6{5NQR@H=f7Szybqt+@sDWk+4dviqLnu>5 +zB8YPsV`3(o9Q=IGT7Y=1B%hT7(hoq_Fgv9dm8P)-7kv$ScsHrVJm)(|X3hG2R7FuU +zxj)I9@8KDp^L6ZLp)edL+qMCU3ZdzRjZaXLNmywZR%wl@C}Pb4shw+~Qe#PcPF`Or +zz3ts(yD@qcP7Cc)TX+->We~zjsTeWwg*?2gw7k5GPe1(>OE+)w*@vquH_xstE&dIj +z;feM$&cA`3&3?D=!fTG%kC!fdd|&>QW2ucEiU82so6AaRT)yz}{e%Fs1&Ip`5G(?l +z^^P8y|H(TOlT$CSqYfV);N_Q3{71kroSp`ugy012r*#xbQ{93Pe~`T4%f`&1q^}Da0u}2)_E2qAS>d +zH^}#=z)jr+K1ZB>EBhD(^BD1Q(NC9xyei9pE7X1E8>Yrhs;UI%sdy{V!Wac>GguR* +zqyzZo+BKX%e;&)r%klY#o9h>De*5*Ga6j2EFdpZc(FLEI-~mzp{{B5PYkk;GX=nqWs!Mb6TGrZ_T$E+K@GJl_ +z3uCN!NEQ}Hb86O5WH(J0B~q>YIGY$A8g!M=2Yj{|_`es6%P93Eh5>L8{LPGNjv0#+Lw7#~Nkm-z+|&l@#U=%Caurioko>aJn>0Y1rtW@ZHT +zsOgFVa4c$~qff3wh8ln{=|tw}t01uI*fxMzIx5Y3kp6JGSHUT-T-Er0G@k`%nPyB% +zjmI|OZ;Xk{He{RCeBXjGgjDa;hu6l>$)Ky`Ei(T<_5rmx`&(itGD#=eIpTd&rNr~O +zm(hS!;6^~u-yF_4tghaMRvK$}?_zUf18aBhVtHu^ckbM&t*$Vp%A#1^SYNwv=ho84 +zqv00k?ytc!JU`Rir`9Lqr|bMV0Ql)kXB^+B&O`(*UD#s*aQyjKM2D0i54(=hU-|N% +zX^4=xfD9GVBA5um%5$eO4e)c4-JtLw5Y?suciy&xO0$Ta;U<~r%F!=nw0BTX> +zu%@25*8~ykwj`s+eyi_Q=EJSU;c(-dduw+tZQQ%_H9Ui_!<+Zq?up~n_%g(|t-SY% +zQ?EK1d7ukN@OsZJfQ3`92LGuJxv@hSCTp<<(omrk1e{Prt3Yma8O8wgCngU)Gd4ak +zZ?oROX4$m1*`#t%#%9x6Yr_HFhRSomkojEz2-zB1iQ)~q&J<(X6Pc;;zt9FnSwJga +z)4g&m_~5hivMdv;A-Vd7K`6yY1zdw+i3@10<1u0laxOeWmjoaA%u;Xwbr?_fvlKG? +z3?S59)Qx!uHFJpZ2v>+ckmKs9k|Dow&AgwAN8rQ;NpE +zj&4%;p>b~)@fdlph)vMS4DKf!$~+&gLMex;x;GjQSBqkFdu#pP;>P;gH_o{&gf%k4 +zww`P}K0=f6(-2?O`U@|^YI^< +z)^EJTJ`@1)7GmEf2q+AsP+&MQJ!Zr?Qu}wz-_dSJ3IVzS6a-Qr&^<_x?WFA=IpfC{ +zc$A4iyCjbk)r~=B_4Td%cNVQpH`VgF&F}97Ku5cwlu0EXAO(W_oNn^}@>=QVADs4D +z008X`AO%8&re*MbpV6}KsH5G`_X&afeNMGKu*XaBm{Oc>Gz$sZ=?Mt07yGo1;EaXo<gZup8#C{;Z#!q-(S8*GlG++nwHyaJ +z#_#Bdp`Dm|Uo(3f{XYnRHu}+YKt~\n" ++"Language-Team: English (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/en/)\n" ++"MIME-Version: 1.0\n" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Content-Transfer-Encoding: 8bit\n" ++"Language: fr\n" ++"Plural-Forms: nplurals=2; plural=(n > 1);\n" ++ ++msgctxt "#30000" ++msgid "File Location" ++msgstr "Emplacement du fichier" ++ ++msgctxt "#30001" ++msgid "Local Path (include Local Network)" ++msgstr "Chemin local (inclue le réseau local)" ++ ++msgctxt "#30002" ++msgid "Remote Path (Internet address)" ++msgstr "Chemin d'accès (adresse internet)" ++ ++msgctxt "#30010" ++msgid "Play List Settings" ++msgstr "Paramètres de la Playlist" ++ ++msgctxt "#30011" ++msgid "M3U Play List Path" ++msgstr "Chemin de la Playlist M3U" ++ ++msgctxt "#30012" ++msgid "M3U Play List URL" ++msgstr "URL de la playlist M3U" ++ ++msgctxt "#30020" ++msgid "EPG Settings" ++msgstr "Paramètres EPG" ++ ++msgctxt "#30021" ++msgid "XMLTV Path" ++msgstr "Chemin du fichier XMLTV" ++ ++msgctxt "#30022" ++msgid "XMLTV URL" ++msgstr "URL du fichier XMLTV" ++ ++msgctxt "#30023" ++msgid "Apply Time Shift To All Channels" ++msgstr "Appliquer un décalage horaire sur toutes les chaînes" ++ ++msgctxt "#30024" ++msgid "EPG Time Shift (hours)" ++msgstr "Décalage horaire de l'EPG (heures)" ++ ++msgctxt "#30030" ++msgid "Channels Logos" ++msgstr "Logos des chaînes" ++ ++msgctxt "#30031" ++msgid "Channels Logos Folder" ++msgstr "Répertoire des Logos de chaînes" +diff --git a/addons/pvr.iptvsimple/addon/resources/language/Russian/strings.po b/addons/pvr.iptvsimple/addon/resources/language/Russian/strings.po +new file mode 100644 +index 0000000..a7f577f +--- /dev/null ++++ b/addons/pvr.iptvsimple/addon/resources/language/Russian/strings.po +@@ -0,0 +1,70 @@ ++# XBMC Media Center language file ++# Addon Name: PVR IP-TV Simple Client ++# Addon id: pvr.iptvsimple ++# Addon version: 0.1.0 ++# Addon Provider: nightik ++msgid "" ++msgstr "" ++"Project-Id-Version: XBMC Main Translation Project (Frodo)\n" ++"Report-Msgid-Bugs-To: http://trac.xbmc.org/\n" ++"POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" ++"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" ++"Last-Translator: XBMC Translation Team\n" ++"Language-Team: Russian (http://www.transifex.com/projects/p/XBMC-Main-Frodo/language/ru/)\n" ++"MIME-Version: 1.0\n" ++"Content-Type: text/plain; charset=UTF-8\n" ++"Content-Transfer-Encoding: 8bit\n" ++"Language: ru\n" ++"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" ++ ++msgctxt "#30000" ++msgid "File Location" ++msgstr "Расположение файла" ++ ++msgctxt "#30001" ++msgid "Local Path (include Local Network)" ++msgstr "Локальный путь (в т.ч. Локальная Сеть)" ++ ++msgctxt "#30002" ++msgid "Remote Path (Internet address)" ++msgstr "Удалённый путь (сеть Интернет)" ++ ++msgctxt "#30010" ++msgid "Play List Settings" ++msgstr "Установки плейлиста" ++ ++msgctxt "#30011" ++msgid "M3U Play List Path" ++msgstr "Путь к M3U" ++ ++msgctxt "#30012" ++msgid "M3U Play List URL" ++msgstr "Ссылка на M3U" ++ ++msgctxt "#30020" ++msgid "EPG Settings" ++msgstr "Установки EPG" ++ ++msgctxt "#30021" ++msgid "XMLTV Path" ++msgstr "Путь к XMLTV" ++ ++msgctxt "#30022" ++msgid "XMLTV URL" ++msgstr "Ссылка на XMLTV" ++ ++msgctxt "#30023" ++msgid "Apply Time Shift To All Channels" ++msgstr "Применять сдвиг по времени для всех каналов" ++ ++msgctxt "#30024" ++msgid "EPG Time Shift (hours)" ++msgstr "Сдвиг по времени для EPG (часов)" ++ ++msgctxt "#30030" ++msgid "Channels Logos" ++msgstr "Логотипы каналов" ++ ++msgctxt "#30031" ++msgid "Channels Logos Folder" ++msgstr "Папка с логотипами каналов" +diff --git a/addons/pvr.iptvsimple/addon/resources/settings.xml b/addons/pvr.iptvsimple/addon/resources/settings.xml +new file mode 100644 +index 0000000..63d42a6 +--- /dev/null ++++ b/addons/pvr.iptvsimple/addon/resources/settings.xml +@@ -0,0 +1,18 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ --> ++ ++ ++ ++ ++ +diff --git a/addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj b/addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj +new file mode 100644 +index 0000000..f9c3f89 +--- /dev/null ++++ b/addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj +@@ -0,0 +1,100 @@ ++ ++ ++ ++ ++ Debug ++ Win32 ++ ++ ++ Release ++ Win32 ++ ++ ++ ++ {6C67B057-CE98-4732-AABF-0E374C718815} ++ pvr_iptvsimple ++ pvr.iptvsimple ++ ++ ++ ++ DynamicLibrary ++ true ++ MultiByte ++ ++ ++ DynamicLibrary ++ false ++ true ++ MultiByte ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ..\..\addon\ ++ ++ ++ XBMC_IPTV_Simple_win32 ++ ++ ++ ..\..\addon\ ++ ++ ++ XBMC_IPTV_Simple_win32 ++ ++ ++ ++ Level3 ++ Disabled ++ ..\..\..\..\project\BuildDependencies\include;..\..\..\..\xbmc;..\..\..\..\lib;..\..\..\..\lib\platform\windows ++ _WINDLL;TARGET_WINDOWS;_CRT_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;%(PreprocessorDefinitions) ++ MultiThreadedDebug ++ ++ ++ true ++ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;zlib.lib;%(AdditionalDependencies) ++ ..\..\..\..\project\BuildDependencies\lib ++ ++ ++ ++ ++ Level3 ++ MaxSpeed ++ true ++ true ++ ..\..\..\..\project\BuildDependencies\include;..\..\..\..\xbmc;..\..\..\..\lib;..\..\..\..\lib\platform\windows ++ _WINDLL;TARGET_WINDOWS;_CRT_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;%(PreprocessorDefinitions) ++ MultiThreaded ++ ++ ++ true ++ true ++ true ++ kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;zlib.lib;%(AdditionalDependencies) ++ ..\..\..\..\project\BuildDependencies\lib ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ {fe4573f6-a794-4ad3-b37f-49e51f1140e6} ++ ++ ++ ++ ++ ++ +\ No newline at end of file +diff --git a/addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj.filters b/addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj.filters +new file mode 100644 +index 0000000..b5146e6 +--- /dev/null ++++ b/addons/pvr.iptvsimple/project/VS2010Express/pvr.iptvsimple.vcxproj.filters +@@ -0,0 +1,33 @@ ++ ++ ++ ++ ++ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} ++ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx ++ ++ ++ {93995380-89BD-4b04-88EB-625FBE52EBFB} ++ h;hpp;hxx;hm;inl;inc;xsd ++ ++ ++ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} ++ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms ++ ++ ++ ++ ++ Source Files ++ ++ ++ Source Files ++ ++ ++ ++ ++ Header Files ++ ++ ++ Header Files ++ ++ ++ +\ No newline at end of file +diff --git a/addons/pvr.iptvsimple/src/PVRIptvData.cpp b/addons/pvr.iptvsimple/src/PVRIptvData.cpp +new file mode 100644 +index 0000000..4ca5265 +--- /dev/null ++++ b/addons/pvr.iptvsimple/src/PVRIptvData.cpp +@@ -0,0 +1,977 @@ ++/* ++ * Copyright (C) 2013 Anton Fedchin ++ * http://github.com/afedchin/xbmc-addon-iptvsimple/ ++ * ++ * Copyright (C) 2011 Pulse-Eight ++ * http://www.pulse-eight.com/ ++ * ++ * 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, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include "zlib.h" ++#include "rapidxml/rapidxml.hpp" ++#include "PVRIptvData.h" ++ ++#define M3U_START_MARKER "#EXTM3U" ++#define M3U_INFO_MARKER "#EXTINF" ++#define TVG_INFO_ID_MARKER "tvg-id=" ++#define TVG_INFO_NAME_MARKER "tvg-name=" ++#define TVG_INFO_LOGO_MARKER "tvg-logo=" ++#define TVG_INFO_SHIFT_MARKER "tvg-shift=" ++#define GROUP_NAME_MARKER "group-title=" ++#define RADIO_MARKER "radio=" ++#define CHANNEL_LOGO_EXTENSION ".png" ++#define SECONDS_IN_DAY 86400 ++ ++using namespace std; ++using namespace ADDON; ++using namespace rapidxml; ++ ++template ++inline bool GetNodeValue(const xml_node * pRootNode, const char* strTag, CStdString& strStringValue) ++{ ++ xml_node *pChildNode = pRootNode->first_node(strTag); ++ if (pChildNode == NULL) ++ { ++ return false; ++ } ++ strStringValue = pChildNode->value(); ++ return true; ++} ++ ++template ++inline bool GetAttributeValue(const xml_node * pNode, const char* strAttributeName, CStdString& strStringValue) ++{ ++ xml_attribute *pAttribute = pNode->first_attribute(strAttributeName); ++ if (pAttribute == NULL) ++ { ++ return false; ++ } ++ strStringValue = pAttribute->value(); ++ return true; ++} ++ ++PVRIptvData::PVRIptvData(void) ++{ ++ m_strXMLTVUrl = g_strTvgPath; ++ m_strM3uUrl = g_strM3UPath; ++ m_strLogoPath = g_strLogoPath; ++ m_iEPGTimeShift = g_iEPGTimeShift; ++ m_bTSOverride = g_bTSOverride; ++ m_iLastStart = 0; ++ m_iLastEnd = 0; ++ ++ m_bEGPLoaded = false; ++ ++ if (LoadPlayList()) ++ { ++ XBMC->QueueNotification(QUEUE_INFO, "%d channels loaded.", m_channels.size()); ++ } ++} ++ ++void *PVRIptvData::Process(void) ++{ ++ return NULL; ++} ++ ++PVRIptvData::~PVRIptvData(void) ++{ ++ m_channels.clear(); ++ m_groups.clear(); ++ m_epg.clear(); ++} ++ ++bool PVRIptvData::LoadEPG(time_t iStart, time_t iEnd) ++{ ++ if (m_strXMLTVUrl.IsEmpty()) ++ { ++ XBMC->Log(LOG_NOTICE, "EPG file path is not configured. EPG not loaded."); ++ m_bEGPLoaded = true; ++ return false; ++ } ++ ++ std::string data; ++ std::string decompressed; ++ int iReaded = 0; ++ ++ int iCount = 0; ++ while(iCount < 3) // max 3 tries ++ { ++ if ((iReaded = GetCachedFileContents(TVG_FILE_NAME, m_strXMLTVUrl, data)) != 0) ++ { ++ break; ++ } ++ XBMC->Log(LOG_ERROR, "Unable to load EPG file '%s': file is missing or empty. :%dth try.", m_strXMLTVUrl.c_str(), ++iCount); ++ if (iCount < 3) ++ { ++ usleep(5 * 1000 * 1000); // sleep 5 sec before next try. ++ } ++ } ++ ++ if (iReaded == 0) ++ { ++ XBMC->Log(LOG_ERROR, "Unable to load EPG file '%s': file is missing or empty. After %d tries.", m_strXMLTVUrl.c_str(), iCount); ++ m_bEGPLoaded = true; ++ return false; ++ } ++ ++ char * buffer; ++ ++ // gzip packed ++ if (data[0] == '\x1F' && data[1] == '\x8B' && data[2] == '\x08') ++ { ++ if (!GzipInflate(data, decompressed)) ++ { ++ XBMC->Log(LOG_ERROR, "Invalid EPG file '%s': unable to decompress file.", m_strXMLTVUrl.c_str()); ++ m_bEGPLoaded = true; ++ return false; ++ } ++ buffer = &(decompressed[0]); ++ } ++ else ++ { ++ buffer = &(data[0]); ++ } ++ ++ // xml should starts with 'Log(LOG_ERROR, "Invalid EPG file '%s': unable to decompress file.", m_strXMLTVUrl.c_str()); ++ m_bEGPLoaded = true; ++ return false; ++ } ++ } ++ ++ xml_document<> xmlDoc; ++ try ++ { ++ xmlDoc.parse<0>(buffer); ++ } ++ catch(parse_error p) ++ { ++ XBMC->Log(LOG_ERROR, "Unable parse EPG XML: %s", p.what()); ++ m_bEGPLoaded = true; ++ return false; ++ } ++ ++ xml_node<> *pRootElement = xmlDoc.first_node("tv"); ++ if (!pRootElement) ++ { ++ XBMC->Log(LOG_ERROR, "Invalid EPG XML: no tag found"); ++ m_bEGPLoaded = true; ++ return false; ++ } ++ ++ // clear previously loaded epg ++ if (m_epg.size() > 0) ++ { ++ m_epg.clear(); ++ } ++ ++ int iBroadCastId = 0; ++ xml_node<> *pChannelNode = NULL; ++ for(pChannelNode = pRootElement->first_node("channel"); pChannelNode; pChannelNode = pChannelNode->next_sibling("channel")) ++ { ++ CStdString strName; ++ CStdString strId; ++ if(!GetAttributeValue(pChannelNode, "id", strId)) ++ { ++ continue; ++ } ++ GetNodeValue(pChannelNode, "display-name", strName); ++ ++ if (FindChannel(strId, strName) == NULL) ++ { ++ continue; ++ } ++ ++ PVRIptvEpgChannel epgChannel; ++ epgChannel.strId = strId; ++ epgChannel.strName = strName; ++ ++ m_epg.push_back(epgChannel); ++ } ++ ++ if (m_epg.size() == 0) ++ { ++ XBMC->Log(LOG_ERROR, "EPG channels not found."); ++ return false; ++ } ++ ++ int iMinShiftTime = m_iEPGTimeShift; ++ int iMaxShiftTime = m_iEPGTimeShift; ++ if (!m_bTSOverride) ++ { ++ iMinShiftTime = SECONDS_IN_DAY; ++ iMaxShiftTime = -SECONDS_IN_DAY; ++ ++ vector::iterator it; ++ for (it = m_channels.begin(); it < m_channels.end(); it++) ++ { ++ if (it->iTvgShift + m_iEPGTimeShift < iMinShiftTime) ++ iMinShiftTime = it->iTvgShift + m_iEPGTimeShift; ++ if (it->iTvgShift + m_iEPGTimeShift > iMaxShiftTime) ++ iMaxShiftTime = it->iTvgShift + m_iEPGTimeShift; ++ } ++ } ++ ++ CStdString strEmpty = ""; ++ PVRIptvEpgChannel *epg = NULL; ++ for(pChannelNode = pRootElement->first_node("programme"); pChannelNode; pChannelNode = pChannelNode->next_sibling("programme")) ++ { ++ CStdString strId; ++ if (!GetAttributeValue(pChannelNode, "channel", strId)) ++ continue; ++ ++ if (epg == NULL || epg->strId != strId) ++ { ++ if ((epg = FindEpg(strId)) == NULL) ++ continue; ++ } ++ ++ CStdString strStart; ++ CStdString strStop; ++ ++ if (!GetAttributeValue(pChannelNode, "start", strStart) || !GetAttributeValue(pChannelNode, "stop", strStop)) ++ { ++ continue; ++ } ++ ++ int iTmpStart = ParseDateTime(strStart); ++ int iTmpEnd = ParseDateTime(strStop); ++ ++ if ((iTmpEnd + iMaxShiftTime < iStart) || (iTmpStart + iMinShiftTime > iEnd)) ++ { ++ continue; ++ } ++ ++ CStdString strTitle; ++ CStdString strCategory; ++ CStdString strDesc; ++ ++ GetNodeValue(pChannelNode, "title", strTitle); ++ GetNodeValue(pChannelNode, "category", strCategory); ++ GetNodeValue(pChannelNode, "desc", strDesc); ++ ++ PVRIptvEpgEntry entry; ++ entry.iBroadcastId = ++iBroadCastId; ++ entry.iGenreType = 0; ++ entry.iGenreSubType = 0; ++ entry.strTitle = strTitle; ++ entry.strPlot = strDesc; ++ entry.strPlotOutline = ""; ++ entry.startTime = iTmpStart; ++ entry.endTime = iTmpEnd; ++ entry.strGenreString = strCategory; ++ ++ epg->epg.push_back(entry); ++ } ++ ++ xmlDoc.clear(); ++ m_bEGPLoaded = true; ++ ++ XBMC->Log(LOG_NOTICE, "EPG Loaded."); ++ ++ return true; ++} ++ ++bool PVRIptvData::LoadPlayList(void) ++{ ++ if (m_strM3uUrl.IsEmpty()) ++ { ++ XBMC->Log(LOG_NOTICE, "Playlist file path is not configured. Channels not loaded."); ++ return false; ++ } ++ ++ CStdString strPlaylistContent; ++ if (!GetCachedFileContents(M3U_FILE_NAME, m_strM3uUrl, strPlaylistContent)) ++ { ++ XBMC->Log(LOG_ERROR, "Unable to load playlist file '%s': file is missing or empty.", m_strM3uUrl.c_str()); ++ return false; ++ } ++ ++ std::stringstream stream(strPlaylistContent); ++ ++ /* load channels */ ++ bool bFirst = true; ++ ++ int iUniqueChannelId = 0; ++ int iUniqueGroupId = 0; ++ int iCurrentGroupId = 0; ++ int iChannelNum = 0; ++ int iEPGTimeShift = 0; ++ ++ PVRIptvChannel tmpChannel; ++ tmpChannel.strTvgId = ""; ++ tmpChannel.strChannelName = ""; ++ tmpChannel.strTvgName = ""; ++ tmpChannel.strTvgLogo = ""; ++ tmpChannel.iTvgShift = 0; ++ ++ char szLine[1024]; ++ while(stream.getline(szLine, 1024)) ++ { ++ ++ CStdString strLine = ""; ++ strLine.append(szLine); ++ strLine.TrimRight(" \t\r\n"); ++ strLine.TrimLeft(" \t"); ++ ++ if (strLine.IsEmpty()) ++ { ++ continue; ++ } ++ ++ if (bFirst) ++ { ++ bFirst = false; ++ if (strLine.Left(3) == "\xEF\xBB\xBF") ++ { ++ strLine.Delete(0, 3); ++ } ++ if (strLine.Left((int)strlen(M3U_START_MARKER)) == M3U_START_MARKER) ++ { ++ double fTvgShift = atof(ReadMarkerValue(strLine, TVG_INFO_SHIFT_MARKER)); ++ iEPGTimeShift = (int) (fTvgShift * 3600.0); ++ continue; ++ } ++ else ++ { ++ break; ++ } ++ } ++ ++ if (strLine.Left((int)strlen(M3U_INFO_MARKER)) == M3U_INFO_MARKER) ++ { ++ bool bRadio = false; ++ double fTvgShift = 0; ++ CStdString strChnlName = ""; ++ CStdString strTvgId = ""; ++ CStdString strTvgName = ""; ++ CStdString strTvgLogo = ""; ++ CStdString strGroupName = ""; ++ CStdString strRadio = ""; ++ ++ // parse line ++ int iColon = (int)strLine.Find(':'); ++ int iComma = (int)strLine.ReverseFind(','); ++ if (iColon >= 0 && iComma >= 0 && iComma > iColon) ++ { ++ // parse name ++ iComma++; ++ strChnlName = strLine.Right((int)strLine.size() - iComma).Trim(); ++ tmpChannel.strChannelName = XBMC->UnknownToUTF8(strChnlName); ++ ++ // parse info ++ CStdString strInfoLine = strLine.Mid(++iColon, --iComma - iColon); ++ ++ strTvgId = ReadMarkerValue(strInfoLine, TVG_INFO_ID_MARKER); ++ strTvgName = ReadMarkerValue(strInfoLine, TVG_INFO_NAME_MARKER); ++ strTvgLogo = ReadMarkerValue(strInfoLine, TVG_INFO_LOGO_MARKER); ++ strGroupName = ReadMarkerValue(strInfoLine, GROUP_NAME_MARKER); ++ strRadio = ReadMarkerValue(strInfoLine, RADIO_MARKER); ++ fTvgShift = atof(ReadMarkerValue(strInfoLine, TVG_INFO_SHIFT_MARKER)); ++ ++ if (strTvgId.IsEmpty()) ++ { ++ char buff[255]; ++ sprintf(buff, "%d", atoi(strInfoLine)); ++ strTvgId.append(buff); ++ } ++ if (strTvgLogo.IsEmpty()) ++ { ++ strTvgLogo = strChnlName; ++ } ++ ++ bRadio = !strRadio.CompareNoCase("true"); ++ tmpChannel.strTvgId = strTvgId; ++ tmpChannel.strTvgName = XBMC->UnknownToUTF8(strTvgName); ++ tmpChannel.strTvgLogo = XBMC->UnknownToUTF8(strTvgLogo); ++ tmpChannel.iTvgShift = (int)(fTvgShift * 3600.0); ++ tmpChannel.bRadio = bRadio; ++ ++ if (tmpChannel.iTvgShift == 0 && iEPGTimeShift != 0) ++ { ++ tmpChannel.iTvgShift = iEPGTimeShift; ++ } ++ ++ if (!strGroupName.IsEmpty()) ++ { ++ strGroupName = XBMC->UnknownToUTF8(strGroupName); ++ ++ PVRIptvChannelGroup * pGroup; ++ if ((pGroup = FindGroup(strGroupName)) == NULL) ++ { ++ PVRIptvChannelGroup group; ++ group.strGroupName = strGroupName; ++ group.iGroupId = ++iUniqueGroupId; ++ group.bRadio = bRadio; ++ ++ m_groups.push_back(group); ++ iCurrentGroupId = iUniqueGroupId; ++ } ++ else ++ { ++ iCurrentGroupId = pGroup->iGroupId; ++ } ++ } ++ } ++ } ++ else if (strLine[0] != '#') ++ { ++ PVRIptvChannel channel; ++ channel.iUniqueId = ++iUniqueChannelId; ++ channel.iChannelNumber = ++iChannelNum; ++ channel.strTvgId = tmpChannel.strTvgId; ++ channel.strChannelName = tmpChannel.strChannelName; ++ channel.strTvgName = tmpChannel.strTvgName; ++ channel.strTvgLogo = tmpChannel.strTvgLogo; ++ channel.iTvgShift = tmpChannel.iTvgShift; ++ channel.bRadio = tmpChannel.bRadio; ++ channel.strStreamURL = strLine; ++ channel.iEncryptionSystem = 0; ++ ++ if (iCurrentGroupId > 0) ++ { ++ channel.bRadio = m_groups.at(iCurrentGroupId - 1).bRadio; ++ m_groups.at(iCurrentGroupId - 1).members.push_back(channel.iChannelNumber); ++ } ++ ++ m_channels.push_back(channel); ++ ++ tmpChannel.strTvgId = ""; ++ tmpChannel.strChannelName = ""; ++ tmpChannel.strTvgName = ""; ++ tmpChannel.strTvgLogo = ""; ++ tmpChannel.iTvgShift = 0; ++ tmpChannel.bRadio = false; ++ } ++ } ++ ++ stream.clear(); ++ ++ if (m_channels.size() == 0) ++ { ++ XBMC->Log(LOG_ERROR, "Unable to load channels from file '%s': file is corrupted.", m_strM3uUrl.c_str()); ++ return false; ++ } ++ ++ ApplyChannelsLogos(); ++ ++ XBMC->Log(LOG_NOTICE, "Loaded %d channels.", m_channels.size()); ++ return true; ++} ++ ++int PVRIptvData::GetChannelsAmount(void) ++{ ++ return m_channels.size(); ++} ++ ++PVR_ERROR PVRIptvData::GetChannels(ADDON_HANDLE handle, bool bRadio) ++{ ++ for (unsigned int iChannelPtr = 0; iChannelPtr < m_channels.size(); iChannelPtr++) ++ { ++ PVRIptvChannel &channel = m_channels.at(iChannelPtr); ++ if (channel.bRadio == bRadio) ++ { ++ PVR_CHANNEL xbmcChannel; ++ memset(&xbmcChannel, 0, sizeof(PVR_CHANNEL)); ++ ++ xbmcChannel.iUniqueId = channel.iUniqueId; ++ xbmcChannel.bIsRadio = channel.bRadio; ++ xbmcChannel.iChannelNumber = channel.iChannelNumber; ++ strncpy(xbmcChannel.strChannelName, channel.strChannelName.c_str(), sizeof(xbmcChannel.strChannelName) - 1); ++ strncpy(xbmcChannel.strStreamURL, channel.strStreamURL.c_str(), sizeof(xbmcChannel.strStreamURL) - 1); ++ xbmcChannel.iEncryptionSystem = channel.iEncryptionSystem; ++ strncpy(xbmcChannel.strIconPath, channel.strLogoPath.c_str(), sizeof(xbmcChannel.strIconPath) - 1); ++ xbmcChannel.bIsHidden = false; ++ ++ PVR->TransferChannelEntry(handle, &xbmcChannel); ++ } ++ } ++ ++ return PVR_ERROR_NO_ERROR; ++} ++ ++bool PVRIptvData::GetChannel(const PVR_CHANNEL &channel, PVRIptvChannel &myChannel) ++{ ++ for (unsigned int iChannelPtr = 0; iChannelPtr < m_channels.size(); iChannelPtr++) ++ { ++ PVRIptvChannel &thisChannel = m_channels.at(iChannelPtr); ++ if (thisChannel.iUniqueId == (int) channel.iUniqueId) ++ { ++ myChannel.iUniqueId = thisChannel.iUniqueId; ++ myChannel.bRadio = thisChannel.bRadio; ++ myChannel.iChannelNumber = thisChannel.iChannelNumber; ++ myChannel.iEncryptionSystem = thisChannel.iEncryptionSystem; ++ myChannel.strChannelName = thisChannel.strChannelName; ++ myChannel.strLogoPath = thisChannel.strLogoPath; ++ myChannel.strStreamURL = thisChannel.strStreamURL; ++ ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++int PVRIptvData::GetChannelGroupsAmount(void) ++{ ++ return m_groups.size(); ++} ++ ++PVR_ERROR PVRIptvData::GetChannelGroups(ADDON_HANDLE handle, bool bRadio) ++{ ++ for (unsigned int iGroupPtr = 0; iGroupPtr < m_groups.size(); iGroupPtr++) ++ { ++ PVRIptvChannelGroup &group = m_groups.at(iGroupPtr); ++ if (group.bRadio == bRadio) ++ { ++ PVR_CHANNEL_GROUP xbmcGroup; ++ memset(&xbmcGroup, 0, sizeof(PVR_CHANNEL_GROUP)); ++ ++ xbmcGroup.bIsRadio = bRadio; ++ strncpy(xbmcGroup.strGroupName, group.strGroupName.c_str(), sizeof(xbmcGroup.strGroupName) - 1); ++ ++ PVR->TransferChannelGroup(handle, &xbmcGroup); ++ } ++ } ++ ++ return PVR_ERROR_NO_ERROR; ++} ++ ++PVR_ERROR PVRIptvData::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) ++{ ++ for (unsigned int iGroupPtr = 0; iGroupPtr < m_groups.size(); iGroupPtr++) ++ { ++ PVRIptvChannelGroup &myGroup = m_groups.at(iGroupPtr); ++ if (!strcmp(myGroup.strGroupName.c_str(),group.strGroupName)) ++ { ++ for (unsigned int iChannelPtr = 0; iChannelPtr < myGroup.members.size(); iChannelPtr++) ++ { ++ int iId = myGroup.members.at(iChannelPtr) - 1; ++ if (iId < 0 || iId > (int)m_channels.size() - 1) ++ continue; ++ PVRIptvChannel &channel = m_channels.at(iId); ++ PVR_CHANNEL_GROUP_MEMBER xbmcGroupMember; ++ memset(&xbmcGroupMember, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); ++ ++ strncpy(xbmcGroupMember.strGroupName, group.strGroupName, sizeof(xbmcGroupMember.strGroupName) - 1); ++ xbmcGroupMember.iChannelUniqueId = channel.iUniqueId; ++ xbmcGroupMember.iChannelNumber = channel.iChannelNumber; ++ ++ PVR->TransferChannelGroupMember(handle, &xbmcGroupMember); ++ } ++ } ++ } ++ ++ return PVR_ERROR_NO_ERROR; ++} ++ ++PVR_ERROR PVRIptvData::GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd) ++{ ++ vector::iterator myChannel; ++ for (myChannel = m_channels.begin(); myChannel < m_channels.end(); myChannel++) ++ { ++ if (myChannel->iUniqueId != (int) channel.iUniqueId) ++ { ++ continue; ++ } ++ ++ if (!m_bEGPLoaded || ++ iStart > m_iLastStart || iEnd > m_iLastEnd) ++ { ++ if (LoadEPG(iStart, iEnd)) ++ { ++ m_iLastStart = iStart; ++ m_iLastEnd = iEnd; ++ } ++ } ++ ++ PVRIptvEpgChannel *epg; ++ if ((epg = FindEpgForChannel(*myChannel)) == NULL ++ || epg->epg.size() == 0) ++ { ++ return PVR_ERROR_NO_ERROR; ++ } ++ ++ int iShift = m_bTSOverride ? m_iEPGTimeShift : myChannel->iTvgShift + m_iEPGTimeShift; ++ ++ vector::iterator myTag; ++ for (myTag = epg->epg.begin(); myTag < epg->epg.end(); myTag++) ++ { ++ if ((myTag->endTime + iShift) < iStart) ++ continue; ++ ++ EPG_TAG tag; ++ memset(&tag, 0, sizeof(EPG_TAG)); ++ ++ tag.iUniqueBroadcastId = myTag->iBroadcastId; ++ tag.strTitle = myTag->strTitle.c_str(); ++ tag.iChannelNumber = myTag->iChannelId; ++ tag.startTime = myTag->startTime + iShift; ++ tag.endTime = myTag->endTime + iShift; ++ tag.strPlotOutline = myTag->strPlotOutline.c_str(); ++ tag.strPlot = myTag->strPlot.c_str(); ++ tag.strIconPath = myTag->strIconPath.c_str(); ++ tag.iGenreType = EPG_GENRE_USE_STRING; //myTag.iGenreType; ++ tag.iGenreSubType = 0; //myTag.iGenreSubType; ++ tag.strGenreDescription = myTag->strGenreString.c_str(); ++ ++ PVR->TransferEpgEntry(handle, &tag); ++ ++ if ((myTag->startTime + iShift) > iEnd) ++ break; ++ } ++ ++ return PVR_ERROR_NO_ERROR; ++ } ++ ++ return PVR_ERROR_NO_ERROR; ++} ++ ++int PVRIptvData::GetFileContents(CStdString& url, std::string &strContent) ++{ ++ strContent.clear(); ++ void* fileHandle = XBMC->OpenFile(url.c_str(), 0); ++ if (fileHandle) ++ { ++ char buffer[1024]; ++ while (int bytesRead = XBMC->ReadFile(fileHandle, buffer, 1024)) ++ strContent.append(buffer, bytesRead); ++ XBMC->CloseFile(fileHandle); ++ } ++ ++ return strContent.length(); ++} ++ ++int PVRIptvData::ParseDateTime(CStdString strDate, bool iDateFormat) ++{ ++ struct tm timeinfo; ++ memset(&timeinfo, 0, sizeof(tm)); ++ ++ if (iDateFormat) ++ { ++ sscanf(strDate, "%04d%02d%02d%02d%02d%02d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); ++ } ++ else ++ { ++ sscanf(strDate, "%02d.%02d.%04d%02d:%02d:%02d", &timeinfo.tm_mday, &timeinfo.tm_mon, &timeinfo.tm_year, &timeinfo.tm_hour, &timeinfo.tm_min, &timeinfo.tm_sec); ++ } ++ ++ timeinfo.tm_mon -= 1; ++ timeinfo.tm_year -= 1900; ++ timeinfo.tm_isdst = -1; ++ ++ return mktime(&timeinfo); ++} ++ ++PVRIptvChannel * PVRIptvData::FindChannel(const std::string &strId, const std::string &strName) ++{ ++ CStdString strTvgName = strName; ++ strTvgName.Replace(' ', '_'); ++ ++ vector::iterator it; ++ for(it = m_channels.begin(); it < m_channels.end(); it++) ++ { ++ if (it->strTvgId == strId) ++ { ++ return &*it; ++ } ++ if (strTvgName == "") ++ { ++ continue; ++ } ++ if (it->strTvgName == strTvgName) ++ { ++ return &*it; ++ } ++ if (it->strChannelName == strName) ++ { ++ return &*it; ++ } ++ } ++ ++ return NULL; ++} ++ ++PVRIptvChannelGroup * PVRIptvData::FindGroup(const std::string &strName) ++{ ++ vector::iterator it; ++ for(it = m_groups.begin(); it < m_groups.end(); it++) ++ { ++ if (it->strGroupName == strName) ++ { ++ return &*it; ++ } ++ } ++ ++ return NULL; ++} ++ ++PVRIptvEpgChannel * PVRIptvData::FindEpg(const std::string &strId) ++{ ++ vector::iterator it; ++ for(it = m_epg.begin(); it < m_epg.end(); it++) ++ { ++ if (it->strId == strId) ++ { ++ return &*it; ++ } ++ } ++ ++ return NULL; ++} ++ ++PVRIptvEpgChannel * PVRIptvData::FindEpgForChannel(PVRIptvChannel &channel) ++{ ++ vector::iterator it; ++ for(it = m_epg.begin(); it < m_epg.end(); it++) ++ { ++ if (it->strId == channel.strTvgId) ++ { ++ return &*it; ++ } ++ CStdString strName = it->strName; ++ strName.Replace(' ', '_'); ++ if (strName == channel.strTvgName ++ || it->strName == channel.strTvgName) ++ { ++ return &*it; ++ } ++ if (it->strName == channel.strChannelName) ++ { ++ return &*it; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* ++ * This method uses zlib to decompress a gzipped file in memory. ++ * Author: Andrew Lim Chong Liang ++ * http://windrealm.org ++ */ ++bool PVRIptvData::GzipInflate( const std::string& compressedBytes, std::string& uncompressedBytes ) { ++ ++#define HANDLE_CALL_ZLIB(status) { \ ++ if(status != Z_OK) { \ ++ free(uncomp); \ ++ return false; \ ++ } \ ++} ++ ++ if ( compressedBytes.size() == 0 ) ++ { ++ uncompressedBytes = compressedBytes ; ++ return true ; ++ } ++ ++ uncompressedBytes.clear() ; ++ ++ unsigned full_length = compressedBytes.size() ; ++ unsigned half_length = compressedBytes.size() / 2; ++ ++ unsigned uncompLength = full_length ; ++ char* uncomp = (char*) calloc( sizeof(char), uncompLength ); ++ ++ z_stream strm; ++ strm.next_in = (Bytef *) compressedBytes.c_str(); ++ strm.avail_in = compressedBytes.size() ; ++ strm.total_out = 0; ++ strm.zalloc = Z_NULL; ++ strm.zfree = Z_NULL; ++ ++ bool done = false ; ++ ++ HANDLE_CALL_ZLIB(inflateInit2(&strm, (16+MAX_WBITS))); ++ ++ while (!done) ++ { ++ // If our output buffer is too small ++ if (strm.total_out >= uncompLength ) ++ { ++ // Increase size of output buffer ++ uncomp = (char *) realloc(uncomp, uncompLength + half_length); ++ if (uncomp == NULL) ++ return false; ++ uncompLength += half_length ; ++ } ++ ++ strm.next_out = (Bytef *) (uncomp + strm.total_out); ++ strm.avail_out = uncompLength - strm.total_out; ++ ++ // Inflate another chunk. ++ int err = inflate (&strm, Z_SYNC_FLUSH); ++ if (err == Z_STREAM_END) ++ done = true; ++ else if (err != Z_OK) ++ { ++ break; ++ } ++ } ++ ++ HANDLE_CALL_ZLIB(inflateEnd (&strm)); ++ ++ for ( size_t i=0; iFileExists(strCachedPath, false)) ++ { ++ struct __stat64 statCached; ++ struct __stat64 statOrig; ++ ++ XBMC->StatFile(strCachedPath, &statCached); ++ XBMC->StatFile(strFilePath, &statOrig); ++ ++ bNeedReload = statCached.st_mtime < statOrig.st_mtime; ++ } ++ else ++ { ++ bNeedReload = true; ++ } ++ ++ if (bNeedReload) ++ { ++ GetFileContents(strFilePath, strContents); ++ if (strContents.length() > 0) ++ { ++ void* fileHandle = XBMC->OpenFileForWrite(strCachedPath, true); ++ if (fileHandle) ++ { ++ XBMC->WriteFile(fileHandle, strContents.c_str(), strContents.length()); ++ XBMC->CloseFile(fileHandle); ++ } ++ } ++ return strContents.length(); ++ } ++ ++ return GetFileContents(strCachedPath, strContents); ++} ++ ++void PVRIptvData::ApplyChannelsLogos() ++{ ++ if (m_strLogoPath.IsEmpty()) ++ { ++ return; ++ } ++ ++ vector::iterator channel; ++ for(channel = m_channels.begin(); channel < m_channels.end(); channel++) ++ { ++ channel->strLogoPath = PathCombine(m_strLogoPath, channel->strTvgLogo); ++ channel->strLogoPath.append(CHANNEL_LOGO_EXTENSION); ++ } ++} ++ ++void PVRIptvData::ReaplyChannelsLogos(const char * strNewPath) ++{ ++ if (strlen(strNewPath) > 0) ++ { ++ m_strLogoPath = strNewPath; ++ ApplyChannelsLogos(); ++ ++ PVR->TriggerChannelUpdate(); ++ PVR->TriggerChannelGroupsUpdate(); ++ } ++} ++ ++void PVRIptvData::ReloadEPG(const char * strNewPath) ++{ ++ if (strNewPath != m_strXMLTVUrl) ++ { ++ m_strXMLTVUrl = strNewPath; ++ m_bEGPLoaded = false; ++ // TODO clear epg for all channels ++ ++ if (LoadEPG(m_iLastStart, m_iLastEnd)) ++ { ++ for(unsigned int iChannelPtr = 0, max = m_channels.size(); iChannelPtr < max; iChannelPtr++) ++ { ++ PVRIptvChannel &myChannel = m_channels.at(iChannelPtr); ++ PVR->TriggerEpgUpdate(myChannel.iUniqueId); ++ } ++ } ++ } ++} ++ ++void PVRIptvData::ReloadPlayList(const char * strNewPath) ++{ ++ if (strNewPath != m_strM3uUrl) ++ { ++ m_strM3uUrl = strNewPath; ++ m_channels.clear(); ++ ++ if (LoadPlayList()) ++ { ++ PVR->TriggerChannelUpdate(); ++ PVR->TriggerChannelGroupsUpdate(); ++ } ++ } ++} ++ ++CStdString PVRIptvData::ReadMarkerValue(std::string &strLine, const char* strMarkerName) ++{ ++ int iMarkerStart = (int) strLine.find(strMarkerName); ++ if (iMarkerStart >= 0) ++ { ++ std::string strMarker = strMarkerName; ++ iMarkerStart += strMarker.length(); ++ if (iMarkerStart < (int)strLine.length()) ++ { ++ char cFind = ' '; ++ if (strLine[iMarkerStart] == '"') ++ { ++ cFind = '"'; ++ iMarkerStart++; ++ } ++ int iMarkerEnd = (int)strLine.find(cFind, iMarkerStart); ++ if (iMarkerEnd < 0) ++ { ++ iMarkerEnd = strLine.length(); ++ } ++ return strLine.substr(iMarkerStart, iMarkerEnd - iMarkerStart); ++ } ++ } ++ ++ return std::string(""); ++} +diff --git a/addons/pvr.iptvsimple/src/PVRIptvData.h b/addons/pvr.iptvsimple/src/PVRIptvData.h +new file mode 100644 +index 0000000..944a734 +--- /dev/null ++++ b/addons/pvr.iptvsimple/src/PVRIptvData.h +@@ -0,0 +1,122 @@ ++#pragma once ++/* ++ * Copyright (C) 2013 Anton Fedchin ++ * http://github.com/afedchin/xbmc-addon-iptvsimple/ ++ * ++ * Copyright (C) 2011 Pulse-Eight ++ * http://www.pulse-eight.com/ ++ * ++ * 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, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include ++#include "platform/util/StdString.h" ++#include "client.h" ++#include "platform/threads/threads.h" ++ ++struct PVRIptvEpgEntry ++{ ++ int iBroadcastId; ++ int iChannelId; ++ int iGenreType; ++ int iGenreSubType; ++ time_t startTime; ++ time_t endTime; ++ std::string strTitle; ++ std::string strPlotOutline; ++ std::string strPlot; ++ std::string strIconPath; ++ std::string strGenreString; ++}; ++ ++struct PVRIptvEpgChannel ++{ ++ std::string strId; ++ std::string strName; ++ std::vector epg; ++}; ++ ++struct PVRIptvChannel ++{ ++ bool bRadio; ++ int iUniqueId; ++ int iChannelNumber; ++ int iEncryptionSystem; ++ int iTvgShift; ++ std::string strChannelName; ++ std::string strLogoPath; ++ std::string strStreamURL; ++ std::string strTvgId; ++ std::string strTvgName; ++ std::string strTvgLogo; ++}; ++ ++struct PVRIptvChannelGroup ++{ ++ bool bRadio; ++ int iGroupId; ++ std::string strGroupName; ++ std::vector members; ++}; ++ ++class PVRIptvData : public PLATFORM::CThread ++{ ++public: ++ PVRIptvData(void); ++ virtual ~PVRIptvData(void); ++ ++ virtual int GetChannelsAmount(void); ++ virtual PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio); ++ virtual bool GetChannel(const PVR_CHANNEL &channel, PVRIptvChannel &myChannel); ++ virtual int GetChannelGroupsAmount(void); ++ virtual PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio); ++ virtual PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group); ++ virtual PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd); ++ virtual void ReaplyChannelsLogos(const char * strNewPath); ++ virtual void ReloadPlayList(const char * strNewPath); ++ virtual void ReloadEPG(const char * strNewPath); ++ ++protected: ++ virtual bool LoadPlayList(void); ++ virtual bool LoadEPG(time_t iStart, time_t iEnd); ++ virtual int GetFileContents(CStdString& url, std::string &strContent); ++ virtual PVRIptvChannel *FindChannel(const std::string &strId, const std::string &strName); ++ virtual PVRIptvChannelGroup *FindGroup(const std::string &strName); ++ virtual PVRIptvEpgChannel *FindEpg(const std::string &strId); ++ virtual PVRIptvEpgChannel *FindEpgForChannel(PVRIptvChannel &channel); ++ virtual int ParseDateTime(CStdString strDate, bool iDateFormat = true); ++ virtual bool GzipInflate( const std::string &compressedBytes, std::string &uncompressedBytes); ++ virtual int GetCachedFileContents(const std::string &strCachedName, const std::string &strFilePath, std::string &strContent); ++ virtual void ApplyChannelsLogos(); ++ virtual CStdString ReadMarkerValue(std::string &strLine, const char * strMarkerName); ++ ++protected: ++ virtual void *Process(void); ++ ++private: ++ bool m_bTSOverride; ++ bool m_bEGPLoaded; ++ int m_iEPGTimeShift; ++ int m_iLastStart; ++ int m_iLastEnd; ++ CStdString m_strXMLTVUrl; ++ CStdString m_strM3uUrl; ++ CStdString m_strLogoPath; ++ std::vector m_groups; ++ std::vector m_channels; ++ std::vector m_epg; ++}; +diff --git a/addons/pvr.iptvsimple/src/client.cpp b/addons/pvr.iptvsimple/src/client.cpp +new file mode 100644 +index 0000000..b80f483 +--- /dev/null ++++ b/addons/pvr.iptvsimple/src/client.cpp +@@ -0,0 +1,438 @@ ++/* ++ * Copyright (C) 2013 Anton Fedchin ++ * http://github.com/afedchin/xbmc-addon-iptvsimple/ ++ * ++ * Copyright (C) 2011 Pulse-Eight ++ * http://www.pulse-eight.com/ ++ * ++ * 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, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include "client.h" ++#include "xbmc_pvr_dll.h" ++#include "PVRIptvData.h" ++#include "platform/util/util.h" ++ ++using namespace std; ++using namespace ADDON; ++ ++#ifdef TARGET_WINDOWS ++#define snprintf _snprintf ++#endif ++ ++bool m_bCreated = false; ++ADDON_STATUS m_CurStatus = ADDON_STATUS_UNKNOWN; ++PVRIptvData *m_data = NULL; ++bool m_bIsPlaying = false; ++PVRIptvChannel m_currentChannel; ++ ++/* User adjustable settings are saved here. ++ * Default values are defined inside client.h ++ * and exported to the other source files. ++ */ ++std::string g_strUserPath = ""; ++std::string g_strClientPath = ""; ++ ++CHelper_libXBMC_addon *XBMC = NULL; ++CHelper_libXBMC_pvr *PVR = NULL; ++ ++std::string g_strTvgPath = ""; ++std::string g_strM3UPath = ""; ++std::string g_strLogoPath = ""; ++int g_iEPGTimeShift = 0; ++bool g_bTSOverride = true; ++ ++extern std::string PathCombine(const std::string &strPath, const std::string &strFileName) ++{ ++ std::string strResult = strPath; ++ if (strResult.at(strResult.size() - 1) == '\\' || ++ strResult.at(strResult.size() - 1) == '/') ++ { ++ strResult.append(strFileName); ++ } ++ else ++ { ++ strResult.append("/"); ++ strResult.append(strFileName); ++ } ++ ++ return strResult; ++} ++ ++extern std::string GetClientFilePath(const std::string &strFileName) ++{ ++ return PathCombine(g_strClientPath, strFileName); ++} ++ ++extern std::string GetUserFilePath(const std::string &strFileName) ++{ ++ return PathCombine(g_strUserPath, strFileName); ++} ++ ++extern "C" { ++ ++void ADDON_ReadSettings(void) ++{ ++ char buffer[1024]; ++ int iPathType = 0; ++ if (!XBMC->GetSetting("m3uPathType", &iPathType)) ++ { ++ iPathType = 1; ++ } ++ CStdString strSettingName = iPathType ? "m3uUrl" : "m3uPath"; ++ if (XBMC->GetSetting(strSettingName, &buffer)) ++ { ++ g_strM3UPath = buffer; ++ } ++ if (g_strM3UPath == "") ++ { ++ g_strM3UPath = GetClientFilePath(M3U_FILE_NAME); ++ } ++ ++ if (!XBMC->GetSetting("epgPathType", &iPathType)) ++ { ++ iPathType = 1; ++ } ++ strSettingName = iPathType ? "epgUrl" : "epgPath"; ++ if (XBMC->GetSetting(strSettingName, &buffer)) ++ { ++ g_strTvgPath = buffer; ++ } ++ // BUG! xbmc does not return slider value ++ //float dTimeShift; ++ //if (XBMC->GetSetting("epgTimeShift", &dTimeShift)) ++ //{ ++ // g_iEPGTimeShift = (int)(dTimeShift * 3600.0); // hours to seconds ++ //} ++ int itmpShift; ++ if (XBMC->GetSetting("epgTimeShift_", &itmpShift)) ++ { ++ itmpShift -= 12; ++ g_iEPGTimeShift = (int)(itmpShift * 3600.0); // hours to seconds ++ } ++ if (!XBMC->GetSetting("epgTSOverride", &g_bTSOverride)) ++ { ++ g_bTSOverride = true; ++ } ++ ++ if (XBMC->GetSetting("logoPath", &buffer)) ++ { ++ g_strLogoPath = buffer; ++ } ++ if (g_strLogoPath == "") ++ { ++ g_strLogoPath = GetClientFilePath("icons/"); ++ } ++} ++ ++ADDON_STATUS ADDON_Create(void* hdl, void* props) ++{ ++ if (!hdl || !props) ++ { ++ return ADDON_STATUS_UNKNOWN; ++ } ++ ++ PVR_PROPERTIES* pvrprops = (PVR_PROPERTIES*)props; ++ ++ XBMC = new CHelper_libXBMC_addon; ++ if (!XBMC->RegisterMe(hdl)) ++ { ++ SAFE_DELETE(XBMC); ++ return ADDON_STATUS_PERMANENT_FAILURE; ++ } ++ ++ PVR = new CHelper_libXBMC_pvr; ++ if (!PVR->RegisterMe(hdl)) ++ { ++ SAFE_DELETE(PVR); ++ SAFE_DELETE(XBMC); ++ return ADDON_STATUS_PERMANENT_FAILURE; ++ } ++ ++ XBMC->Log(LOG_DEBUG, "%s - Creating the PVR IPTV Simple add-on", __FUNCTION__); ++ ++ m_CurStatus = ADDON_STATUS_UNKNOWN; ++ g_strUserPath = pvrprops->strUserPath; ++ g_strClientPath = pvrprops->strClientPath; ++ ++ if (!XBMC->DirectoryExists(g_strUserPath.c_str())) ++ { ++#ifdef TARGET_WINDOWS ++ CreateDirectory(g_strUserPath.c_str(), NULL); ++#else ++ XBMC->CreateDirectory(g_strUserPath.c_str()); ++#endif ++ } ++ ++ ADDON_ReadSettings(); ++ ++ m_data = new PVRIptvData; ++ m_CurStatus = ADDON_STATUS_OK; ++ m_bCreated = true; ++ ++ return m_CurStatus; ++} ++ ++ADDON_STATUS ADDON_GetStatus() ++{ ++ return m_CurStatus; ++} ++ ++void ADDON_Destroy() ++{ ++ delete m_data; ++ m_bCreated = false; ++ m_CurStatus = ADDON_STATUS_UNKNOWN; ++} ++ ++bool ADDON_HasSettings() ++{ ++ return true; ++} ++ ++unsigned int ADDON_GetSettings(ADDON_StructSetting ***sSet) ++{ ++ return 0; ++} ++ ++ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) ++{ ++ // reset cache and restart addon ++ ++ string strFile = GetUserFilePath(M3U_FILE_NAME); ++ if (XBMC->FileExists(strFile.c_str(), false)) ++ { ++#ifdef TARGET_WINDOWS ++ DeleteFile(strFile.c_str()); ++#else ++ XBMC->DeleteFile(strFile.c_str()); ++#endif ++ } ++ ++ strFile = GetUserFilePath(TVG_FILE_NAME); ++ if (XBMC->FileExists(strFile.c_str(), false)) ++ { ++#ifdef TARGET_WINDOWS ++ DeleteFile(strFile.c_str()); ++#else ++ XBMC->DeleteFile(strFile.c_str()); ++#endif ++ } ++ ++ return ADDON_STATUS_NEED_RESTART; ++} ++ ++void ADDON_Stop() ++{ ++} ++ ++void ADDON_FreeSettings() ++{ ++} ++ ++void ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data) ++{ ++} ++ ++/*********************************************************** ++ * PVR Client AddOn specific public library functions ++ ***********************************************************/ ++ ++const char* GetPVRAPIVersion(void) ++{ ++ static const char *strApiVersion = XBMC_PVR_API_VERSION; ++ return strApiVersion; ++} ++ ++const char* GetMininumPVRAPIVersion(void) ++{ ++ static const char *strMinApiVersion = XBMC_PVR_MIN_API_VERSION; ++ return strMinApiVersion; ++} ++ ++PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities) ++{ ++ pCapabilities->bSupportsEPG = true; ++ pCapabilities->bSupportsTV = true; ++ pCapabilities->bSupportsRadio = true; ++ pCapabilities->bSupportsChannelGroups = true; ++ pCapabilities->bSupportsRecordings = false; ++ ++ return PVR_ERROR_NO_ERROR; ++} ++ ++const char *GetBackendName(void) ++{ ++ static const char *strBackendName = "IPTV Simple PVR Add-on"; ++ return strBackendName; ++} ++ ++const char *GetBackendVersion(void) ++{ ++ static CStdString strBackendVersion = PVR_CLIENT_VERSION; ++ return strBackendVersion.c_str(); ++} ++ ++const char *GetConnectionString(void) ++{ ++ static CStdString strConnectionString = "connected"; ++ return strConnectionString.c_str(); ++} ++ ++PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed) ++{ ++ *iTotal = 0; ++ *iUsed = 0; ++ return PVR_ERROR_NO_ERROR; ++} ++ ++PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd) ++{ ++ if (m_data) ++ return m_data->GetEPGForChannel(handle, channel, iStart, iEnd); ++ ++ return PVR_ERROR_SERVER_ERROR; ++} ++ ++int GetChannelsAmount(void) ++{ ++ if (m_data) ++ return m_data->GetChannelsAmount(); ++ ++ return -1; ++} ++ ++PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) ++{ ++ if (m_data) ++ return m_data->GetChannels(handle, bRadio); ++ ++ return PVR_ERROR_SERVER_ERROR; ++} ++ ++bool OpenLiveStream(const PVR_CHANNEL &channel) ++{ ++ if (m_data) ++ { ++ CloseLiveStream(); ++ ++ if (m_data->GetChannel(channel, m_currentChannel)) ++ { ++ m_bIsPlaying = true; ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++void CloseLiveStream(void) ++{ ++ m_bIsPlaying = false; ++} ++ ++int GetCurrentClientChannel(void) ++{ ++ return m_currentChannel.iUniqueId; ++} ++ ++bool SwitchChannel(const PVR_CHANNEL &channel) ++{ ++ CloseLiveStream(); ++ ++ return OpenLiveStream(channel); ++} ++ ++PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) ++{ ++ return PVR_ERROR_NOT_IMPLEMENTED; ++} ++ ++int GetChannelGroupsAmount(void) ++{ ++ if (m_data) ++ return m_data->GetChannelGroupsAmount(); ++ ++ return -1; ++} ++ ++PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) ++{ ++ if (m_data) ++ return m_data->GetChannelGroups(handle, bRadio); ++ ++ return PVR_ERROR_SERVER_ERROR; ++} ++ ++PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) ++{ ++ if (m_data) ++ return m_data->GetChannelGroupMembers(handle, group); ++ ++ return PVR_ERROR_SERVER_ERROR; ++} ++ ++PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus) ++{ ++ snprintf(signalStatus.strAdapterName, sizeof(signalStatus.strAdapterName), "IPTV Simple Adapter 1"); ++ snprintf(signalStatus.strAdapterStatus, sizeof(signalStatus.strAdapterStatus), "OK"); ++ ++ return PVR_ERROR_NO_ERROR; ++} ++ ++/** UNUSED API FUNCTIONS */ ++const char * GetLiveStreamURL(const PVR_CHANNEL &channel) { return ""; } ++bool CanPauseStream(void) { return false; } ++int GetRecordingsAmount(void) { return -1; } ++PVR_ERROR GetRecordings(ADDON_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR DialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } ++bool OpenRecordedStream(const PVR_RECORDING &recording) { return false; } ++void CloseRecordedStream(void) {} ++int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; } ++long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */) { return 0; } ++long long PositionRecordedStream(void) { return -1; } ++long long LengthRecordedStream(void) { return 0; } ++void DemuxReset(void) {} ++void DemuxFlush(void) {} ++int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; } ++long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) { return -1; } ++long long PositionLiveStream(void) { return -1; } ++long long LengthLiveStream(void) { return -1; } ++PVR_ERROR DeleteRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR RenameRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING &recording, int count) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; } ++int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) { return -1; } ++int GetTimersAmount(void) { return -1; } ++PVR_ERROR GetTimers(ADDON_HANDLE handle) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR AddTimer(const PVR_TIMER &timer) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete) { return PVR_ERROR_NOT_IMPLEMENTED; } ++PVR_ERROR UpdateTimer(const PVR_TIMER &timer) { return PVR_ERROR_NOT_IMPLEMENTED; } ++void DemuxAbort(void) {} ++DemuxPacket* DemuxRead(void) { return NULL; } ++unsigned int GetChannelSwitchDelay(void) { return 0; } ++void PauseStream(bool bPaused) {} ++bool CanSeekStream(void) { return false; } ++bool SeekTime(int,bool,double*) { return false; } ++void SetSpeed(int) {}; ++} +diff --git a/addons/pvr.iptvsimple/src/client.h b/addons/pvr.iptvsimple/src/client.h +new file mode 100644 +index 0000000..bc81eeb +--- /dev/null ++++ b/addons/pvr.iptvsimple/src/client.h +@@ -0,0 +1,54 @@ ++#pragma once ++/* ++ * Copyright (C) 2013 Anton Fedchin ++ * http://github.com/afedchin/xbmc-addon-iptvsimple/ ++ * ++ * Copyright (C) 2011 Pulse-Eight ++ * http://www.pulse-eight.com/ ++ * ++ * 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, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include "libXBMC_addon.h" ++#include "libXBMC_pvr.h" ++#include "libXBMC_gui.h" ++ ++#define PVR_CLIENT_VERSION "0.1.3" ++#define M3U_FILE_NAME "iptv.m3u.cache" ++#define TVG_FILE_NAME "xmltv.xml.cache" ++ ++/*! ++ * @brief PVR macros for string exchange ++ */ ++#define PVR_STRCPY(dest, source) do { strncpy(dest, source, sizeof(dest)-1); dest[sizeof(dest)-1] = '\0'; } while(0) ++#define PVR_STRCLR(dest) memset(dest, 0, sizeof(dest)) ++ ++extern bool m_bCreated; ++extern std::string g_strUserPath; ++extern std::string g_strClientPath; ++extern ADDON::CHelper_libXBMC_addon *XBMC; ++extern CHelper_libXBMC_pvr *PVR; ++ ++extern std::string g_strM3UPath; ++extern std::string g_strTvgPath; ++extern std::string g_strLogoPath; ++extern int g_iEPGTimeShift; ++extern bool g_bTSOverride; ++ ++extern std::string PathCombine(const std::string &strPath, const std::string &strFileName); ++extern std::string GetClientFilePath(const std::string &strFileName); ++extern std::string GetUserFilePath(const std::string &strFileName); +diff --git a/configure.ac b/configure.ac +index 8ced892..b0de4a6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -167,10 +167,19 @@ if test "$build_addons_with_dependencies" = "yes"; then + AC_DEFINE([HAVE_BOOST],[1],["Define to 1 if we have the `boost' headers"]) + fi + echo "Boost: $use_boost" ++ ++ AC_CHECK_LIB([z],[inflate], use_zlib=yes, use_zlib=no) ++ if test "$use_zlib" = "yes"; then ++ AC_DEFINE([HAVE_ZLIB],[1],["Define to 1 if we have the zlib library"]) ++ ZLIB_LIBS='-lz' ++ AC_SUBST(ZLIB_LIBS) ++ fi ++ echo "Zlib: $use_zlib" + fi + + ### Addons with dependencies + AM_CONDITIONAL([ADDON_MYTHTV], [test "$build_addons_with_dependencies" = "yes" -a "$use_mysql" = "yes" -a "$use_boost" = "yes"]) ++AM_CONDITIONAL([ADDON_IPTVSIMPLE], [test "$build_addons_with_dependencies" = "yes" -a "$use_zlib" = "yes"]) + + ### Check for Intree building + if test "x${cross_compiling}" = "xyes" || test "x${cross_compiling}" = "xmaybe"; then +@@ -272,6 +281,7 @@ AC_CONFIG_FILES([Makefile \ + addons/pvr.vuplus/Makefile \ + addons/pvr.vdr.vnsi/Makefile \ + addons/pvr.mythtv.cmyth/Makefile \ ++ addons/pvr.iptvsimple/Makefile \ + addons/pvr.argustv/addon/addon.xml + addons/pvr.demo/addon/addon.xml + addons/pvr.dvbviewer/addon/addon.xml +@@ -281,6 +291,7 @@ AC_CONFIG_FILES([Makefile \ + addons/pvr.nextpvr/addon/addon.xml + addons/pvr.njoy/addon/addon.xml + addons/pvr.vdr.vnsi/addon/addon.xml +- addons/pvr.vuplus/addon/addon.xml]) ++ addons/pvr.vuplus/addon/addon.xml ++ addons/pvr.iptvsimple/addon/addon.xml]) + + AC_OUTPUT +diff --git a/lib/rapidxml/license.txt b/lib/rapidxml/license.txt +new file mode 100644 +index 0000000..1409831 +--- /dev/null ++++ b/lib/rapidxml/license.txt +@@ -0,0 +1,52 @@ ++Use of this software is granted under one of the following two licenses, ++to be chosen freely by the user. ++ ++1. Boost Software License - Version 1.0 - August 17th, 2003 ++=============================================================================== ++ ++Copyright (c) 2006, 2007 Marcin Kalicinski ++ ++Permission is hereby granted, free of charge, to any person or organization ++obtaining a copy of the software and accompanying documentation covered by ++this license (the "Software") to use, reproduce, display, distribute, ++execute, and transmit the Software, and to prepare derivative works of the ++Software, and to permit third-parties to whom the Software is furnished to ++do so, all subject to the following: ++ ++The copyright notices in the Software and this entire statement, including ++the above license grant, this restriction and the following disclaimer, ++must be included in all copies of the Software, in whole or in part, and ++all derivative works of the Software, unless such copies or derivative ++works are solely in the form of machine-executable object code generated by ++a source language processor. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT ++SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE ++FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ++ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++DEALINGS IN THE SOFTWARE. ++ ++2. The MIT License ++=============================================================================== ++ ++Copyright (c) 2006, 2007 Marcin Kalicinski ++ ++Permission is hereby granted, free of charge, to any person obtaining a copy ++of this software and associated documentation files (the "Software"), to deal ++in the Software without restriction, including without limitation the rights ++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ++of the Software, and to permit persons to whom the Software is furnished to do so, ++subject to the following conditions: ++ ++The above copyright notice and this permission notice shall be included in all ++copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++IN THE SOFTWARE. +diff --git a/lib/rapidxml/manual.html b/lib/rapidxml/manual.html +new file mode 100644 +index 0000000..2c42270 +--- /dev/null ++++ b/lib/rapidxml/manual.html +@@ -0,0 +1,406 @@ ++

RAPIDXML Manual

Version 1.13

Copyright (C) 2006, 2009 Marcin Kalicinski
See accompanying file license.txt for license information.

Table of Contents

1. What is RapidXml?
1.1 Dependencies And Compatibility
1.2 Character Types And Encodings
1.3 Error Handling
1.4 Memory Allocation
1.5 W3C Compliance
1.6 API Design
1.7 Reliability
1.8 Acknowledgements
2. Two Minute Tutorial
2.1 Parsing
2.2 Accessing The DOM Tree
2.3 Modifying The DOM Tree
2.4 Printing XML
3. Differences From Regular XML Parsers
3.1 Lifetime Of Source Text
3.2 Ownership Of Strings
3.3 Destructive Vs Non-Destructive Mode
4. Performance
4.1 Comparison With Other Parsers
5. Reference

1. What is RapidXml?

RapidXml is an attempt to create the fastest XML DOM parser possible, while retaining useability, portability and reasonable W3C compatibility. It is an in-situ parser written in C++, with parsing speed approaching that of strlen() function executed on the same data.

++ Entire parser is contained in a single header file, so no building or linking is neccesary. To use it you just need to copy rapidxml.hpp file to a convenient place (such as your project directory), and include it where needed. You may also want to use printing functions contained in header rapidxml_print.hpp.

1.1 Dependencies And Compatibility

RapidXml has no dependencies other than a very small subset of standard C++ library (<cassert>, <cstdlib>, <new> and <exception>, unless exceptions are disabled). It should compile on any reasonably conformant compiler, and was tested on Visual C++ 2003, Visual C++ 2005, Visual C++ 2008, gcc 3, gcc 4, and Comeau 4.3.3. Care was taken that no warnings are produced on these compilers, even with highest warning levels enabled.

1.2 Character Types And Encodings

RapidXml is character type agnostic, and can work both with narrow and wide characters. Current version does not fully support UTF-16 or UTF-32, so use of wide characters is somewhat incapacitated. However, it should succesfully parse wchar_t strings containing UTF-16 or UTF-32 if endianness of the data matches that of the machine. UTF-8 is fully supported, including all numeric character references, which are expanded into appropriate UTF-8 byte sequences (unless you enable parse_no_utf8 flag).

++ Note that RapidXml performs no decoding - strings returned by name() and value() functions will contain text encoded using the same encoding as source file. Rapidxml understands and expands the following character references: &apos; &amp; &quot; &lt; &gt; &#...; Other character references are not expanded.

1.3 Error Handling

By default, RapidXml uses C++ exceptions to report errors. If this behaviour is undesirable, RAPIDXML_NO_EXCEPTIONS can be defined to suppress exception code. See parse_error class and parse_error_handler() function for more information.

1.4 Memory Allocation

RapidXml uses a special memory pool object to allocate nodes and attributes, because direct allocation using new operator would be far too slow. Underlying memory allocations performed by the pool can be customized by use of memory_pool::set_allocator() function. See class memory_pool for more information.

1.5 W3C Compliance

RapidXml is not a W3C compliant parser, primarily because it ignores DOCTYPE declarations. There is a number of other, minor incompatibilities as well. Still, it can successfully parse and produce complete trees of all valid XML files in W3C conformance suite (over 1000 files specially designed to find flaws in XML processors). In destructive mode it performs whitespace normalization and character entity substitution for a small set of built-in entities.

1.6 API Design

RapidXml API is minimalistic, to reduce code size as much as possible, and facilitate use in embedded environments. Additional convenience functions are provided in separate headers: rapidxml_utils.hpp and rapidxml_print.hpp. Contents of these headers is not an essential part of the library, and is currently not documented (otherwise than with comments in code).

1.7 Reliability

RapidXml is very robust and comes with a large harness of unit tests. Special care has been taken to ensure stability of the parser no matter what source text is thrown at it. One of the unit tests produces 100,000 randomly corrupted variants of XML document, which (when uncorrupted) contains all constructs recognized by RapidXml. RapidXml passes this test when it correctly recognizes that errors have been introduced, and does not crash or loop indefinitely.

++ Another unit test puts RapidXml head-to-head with another, well estabilished XML parser, and verifies that their outputs match across a wide variety of small and large documents.

++ Yet another test feeds RapidXml with over 1000 test files from W3C compliance suite, and verifies that correct results are obtained. There are also additional tests that verify each API function separately, and test that various parsing modes work as expected.

1.8 Acknowledgements

I would like to thank Arseny Kapoulkine for his work on pugixml, which was an inspiration for this project. Additional thanks go to Kristen Wegner for creating pugxml, from which pugixml was derived. Janusz Wohlfeil kindly ran RapidXml speed tests on hardware that I did not have access to, allowing me to expand performance comparison table.

2. Two Minute Tutorial

2.1 Parsing

The following code causes RapidXml to parse a zero-terminated string named text:
using namespace rapidxml;
++xml_document<> doc;    // character type defaults to char
++doc.parse<0>(text);    // 0 means default parse flags
++
doc object is now a root of DOM tree containing representation of the parsed XML. Because all RapidXml interface is contained inside namespace rapidxml, users must either bring contents of this namespace into scope, or fully qualify all the names. Class xml_document represents a root of the DOM hierarchy. By means of public inheritance, it is also an xml_node and a memory_pool. Template parameter of xml_document::parse() function is used to specify parsing flags, with which you can fine-tune behaviour of the parser. Note that flags must be a compile-time constant.

2.2 Accessing The DOM Tree

To access the DOM tree, use methods of xml_node and xml_attribute classes:
cout << "Name of my first node is: " << doc.first_node()->name() << "\n";
++xml_node<> *node = doc.first_node("foobar");
++cout << "Node foobar has value " << node->value() << "\n";
++for (xml_attribute<> *attr = node->first_attribute();
++     attr; attr = attr->next_attribute())
++{
++    cout << "Node foobar has attribute " << attr->name() << " ";
++    cout << "with value " << attr->value() << "\n";
++}
++

2.3 Modifying The DOM Tree

DOM tree produced by the parser is fully modifiable. Nodes and attributes can be added/removed, and their contents changed. The below example creates a HTML document, whose sole contents is a link to google.com website:
xml_document<> doc;
++xml_node<> *node = doc.allocate_node(node_element, "a", "Google");
++doc.append_node(node);
++xml_attribute<> *attr = doc.allocate_attribute("href", "google.com");
++node->append_attribute(attr);
++
One quirk is that nodes and attributes do not own the text of their names and values. This is because normally they only store pointers to the source text. So, when assigning a new name or value to the node, care must be taken to ensure proper lifetime of the string. The easiest way to achieve it is to allocate the string from the xml_document memory pool. In the above example this is not necessary, because we are only assigning character constants. But the code below uses memory_pool::allocate_string() function to allocate node name (which will have the same lifetime as the document), and assigns it to a new node:
xml_document<> doc;
++char *node_name = doc.allocate_string(name);        // Allocate string and copy name into it
++xml_node<> *node = doc.allocate_node(node_element, node_name);  // Set node name to node_name
++
Check Reference section for description of the entire interface.

2.4 Printing XML

You can print xml_document and xml_node objects into an XML string. Use print() function or operator <<, which are defined in rapidxml_print.hpp header.
using namespace rapidxml;
++xml_document<> doc;    // character type defaults to char
++// ... some code to fill the document
++
++// Print to stream using operator <<
++std::cout << doc;   
++
++// Print to stream using print function, specifying printing flags
++print(std::cout, doc, 0);   // 0 means default printing flags
++
++// Print to string using output iterator
++std::string s;
++print(std::back_inserter(s), doc, 0);
++
++// Print to memory buffer using output iterator
++char buffer[4096];                      // You are responsible for making the buffer large enough!
++char *end = print(buffer, doc, 0);      // end contains pointer to character after last printed character
++*end = 0;                               // Add string terminator after XML
++

3. Differences From Regular XML Parsers

RapidXml is an in-situ parser, which allows it to achieve very high parsing speed. In-situ means that parser does not make copies of strings. Instead, it places pointers to the source text in the DOM hierarchy.

3.1 Lifetime Of Source Text

In-situ parsing requires that source text lives at least as long as the document object. If source text is destroyed, names and values of nodes in DOM tree will become destroyed as well. Additionally, whitespace processing, character entity translation, and zero-termination of strings require that source text be modified during parsing (but see non-destructive mode). This makes the text useless for further processing once it was parsed by RapidXml.

++ In many cases however, these are not serious issues.

3.2 Ownership Of Strings

Nodes and attributes produced by RapidXml do not own their name and value strings. They merely hold the pointers to them. This means you have to be careful when setting these values manually, by using xml_base::name(const Ch *) or xml_base::value(const Ch *) functions. Care must be taken to ensure that lifetime of the string passed is at least as long as lifetime of the node/attribute. The easiest way to achieve it is to allocate the string from memory_pool owned by the document. Use memory_pool::allocate_string() function for this purpose.

3.3 Destructive Vs Non-Destructive Mode

By default, the parser modifies source text during the parsing process. This is required to achieve character entity translation, whitespace normalization, and zero-termination of strings.

++ In some cases this behaviour may be undesirable, for example if source text resides in read only memory, or is mapped to memory directly from file. By using appropriate parser flags (parse_non_destructive), source text modifications can be disabled. However, because RapidXml does in-situ parsing, it obviously has the following side-effects:

4. Performance

RapidXml achieves its speed through use of several techniques:
  • In-situ parsing. When building DOM tree, RapidXml does not make copies of string data, such as node names and values. Instead, it stores pointers to interior of the source text.
  • Use of template metaprogramming techniques. This allows it to move much of the work to compile time. Through magic of the templates, C++ compiler generates a separate copy of parsing code for any combination of parser flags you use. In each copy, all possible decisions are made at compile time and all unused code is omitted.
  • Extensive use of lookup tables for parsing.
  • Hand-tuned C++ with profiling done on several most popular CPUs.
This results in a very small and fast code: a parser which is custom tailored to exact needs with each invocation.

4.1 Comparison With Other Parsers

The table below compares speed of RapidXml to some other parsers, and to strlen() function executed on the same data. On a modern CPU (as of 2007), you can expect parsing throughput to be close to 1 GB/s. As a rule of thumb, parsing speed is about 50-100x faster than Xerces DOM, 30-60x faster than TinyXml, 3-12x faster than pugxml, and about 5% - 30% faster than pugixml, the fastest XML parser I know of.
  • The test file is a real-world, 50kB large, moderately dense XML file.
  • All timing is done by using RDTSC instruction present in Pentium-compatible CPUs.
  • No profile-guided optimizations are used.
  • All parsers are running in their fastest modes.
  • The results are given in CPU cycles per character, so frequency of CPUs is irrelevant.
  • The results are minimum values from a large number of runs, to minimize effects of operating system activity, task switching, interrupt handling etc.
  • A single parse of the test file takes about 1/10th of a millisecond, so with large number of runs there is a good chance of hitting at least one no-interrupt streak, and obtaining undisturbed results.
Platform
Compiler
strlen() RapidXml pugixml 0.3 pugxml TinyXml
Pentium 4
MSVC 8.0
2.5
5.4
7.0
61.7
298.8
Pentium 4
gcc 4.1.1
0.8
6.1
9.5
67.0
413.2
Core 2
MSVC 8.0
1.0
4.5
5.0
24.6
154.8
Core 2
gcc 4.1.1
0.6
4.6
5.4
28.3
229.3
Athlon XP
MSVC 8.0
3.1
7.7
8.0
25.5
182.6
Athlon XP
gcc 4.1.1
0.9
8.2
9.2
33.7
265.2
Pentium 3
MSVC 8.0
2.0
6.3
7.0
30.9
211.9
Pentium 3
gcc 4.1.1
1.0
6.7
8.9
35.3
316.0
(*) All results are in CPU cycles per character of source text

5. Reference

This section lists all classes, functions, constants etc. and describes them in detail.
class ++ template ++ rapidxml::memory_pool
++ constructor ++ memory_pool()
++ destructor ++ ~memory_pool()
function allocate_node(node_type type, const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0)
function allocate_attribute(const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0)
function allocate_string(const Ch *source=0, std::size_t size=0)
function clone_node(const xml_node< Ch > *source, xml_node< Ch > *result=0)
function clear()
function set_allocator(alloc_func *af, free_func *ff)

class rapidxml::parse_error
++ constructor ++ parse_error(const char *what, void *where)
function what() const
function where() const

class ++ template ++ rapidxml::xml_attribute
++ constructor ++ xml_attribute()
function document() const
function previous_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const

class ++ template ++ rapidxml::xml_base
++ constructor ++ xml_base()
function name() const
function name_size() const
function value() const
function value_size() const
function name(const Ch *name, std::size_t size)
function name(const Ch *name)
function value(const Ch *value, std::size_t size)
function value(const Ch *value)
function parent() const

class ++ template ++ rapidxml::xml_document
++ constructor ++ xml_document()
function parse(Ch *text)
function clear()

class ++ template ++ rapidxml::xml_node
++ constructor ++ xml_node(node_type type)
function type() const
function document() const
function first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function last_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function previous_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function last_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function type(node_type type)
function prepend_node(xml_node< Ch > *child)
function append_node(xml_node< Ch > *child)
function insert_node(xml_node< Ch > *where, xml_node< Ch > *child)
function remove_first_node()
function remove_last_node()
function remove_node(xml_node< Ch > *where)
function remove_all_nodes()
function prepend_attribute(xml_attribute< Ch > *attribute)
function append_attribute(xml_attribute< Ch > *attribute)
function insert_attribute(xml_attribute< Ch > *where, xml_attribute< Ch > *attribute)
function remove_first_attribute()
function remove_last_attribute()
function remove_attribute(xml_attribute< Ch > *where)
function remove_all_attributes()

namespace rapidxml
enum node_type
function parse_error_handler(const char *what, void *where)
function print(OutIt out, const xml_node< Ch > &node, int flags=0)
function print(std::basic_ostream< Ch > &out, const xml_node< Ch > &node, int flags=0)
function operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node)
++ constant ++ parse_no_data_nodes
++ constant ++ parse_no_element_values
++ constant ++ parse_no_string_terminators
++ constant ++ parse_no_entity_translation
++ constant ++ parse_no_utf8
++ constant ++ parse_declaration_node
++ constant ++ parse_comment_nodes
++ constant ++ parse_doctype_node
++ constant ++ parse_pi_nodes
++ constant ++ parse_validate_closing_tags
++ constant ++ parse_trim_whitespace
++ constant ++ parse_normalize_whitespace
++ constant ++ parse_default
++ constant ++ parse_non_destructive
++ constant ++ parse_fastest
++ constant ++ parse_full
++ constant ++ print_no_indenting


class ++ template ++ rapidxml::memory_pool

++ ++ Defined in rapidxml.hpp
++ Base class for ++ xml_document

Description

This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. In most cases, you will not need to use this class directly. However, if you need to create nodes manually or modify names/values of nodes, you are encouraged to use memory_pool of relevant xml_document to allocate the memory. Not only is this faster than allocating them by using new operator, but also their lifetime will be tied to the lifetime of document, possibly simplyfing memory management.

++ Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. You can also call allocate_string() function to allocate strings. Such strings can then be used as names or values of nodes without worrying about their lifetime. Note that there is no free() function -- all allocations are freed at once when clear() function is called, or when the pool is destroyed.

++ It is also possible to create a standalone memory_pool, and use it to allocate nodes, whose lifetime will not be tied to any document.

++ Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. Until static memory is exhausted, no dynamic memory allocations are done. When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, by using global new[] and delete[] operators. This behaviour can be changed by setting custom allocation routines. Use set_allocator() function to set them.

++ Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. This value defaults to the size of pointer on target architecture.

++ To obtain absolutely top performance from the parser, it is important that all nodes are allocated from a single, contiguous block of memory. Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT to obtain best wasted memory to performance compromise. To do it, define their values before rapidxml.hpp file is included.

Parameters

Ch
Character type of created nodes.

++ constructor ++ memory_pool::memory_pool

Synopsis

memory_pool(); ++

Description

Constructs empty pool with default allocator functions.

++ destructor ++ memory_pool::~memory_pool

Synopsis

~memory_pool(); ++

Description

Destroys pool and frees all the memory. This causes memory occupied by nodes allocated by the pool to be freed. Nodes allocated from the pool are no longer valid.

function memory_pool::allocate_node

Synopsis

xml_node<Ch>* allocate_node(node_type type, const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0); ++

Description

Allocates a new node from the pool, and optionally assigns name and value to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

type
Type of node to create.
name
Name to assign to the node, or 0 to assign no name.
value
Value to assign to the node, or 0 to assign no value.
name_size
Size of name to assign, or 0 to automatically calculate size from name string.
value_size
Size of value to assign, or 0 to automatically calculate size from value string.

Returns

Pointer to allocated node. This pointer will never be NULL.

function memory_pool::allocate_attribute

Synopsis

xml_attribute<Ch>* allocate_attribute(const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0); ++

Description

Allocates a new attribute from the pool, and optionally assigns name and value to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

name
Name to assign to the attribute, or 0 to assign no name.
value
Value to assign to the attribute, or 0 to assign no value.
name_size
Size of name to assign, or 0 to automatically calculate size from name string.
value_size
Size of value to assign, or 0 to automatically calculate size from value string.

Returns

Pointer to allocated attribute. This pointer will never be NULL.

function memory_pool::allocate_string

Synopsis

Ch* allocate_string(const Ch *source=0, std::size_t size=0); ++

Description

Allocates a char array of given size from the pool, and optionally copies a given string to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

source
String to initialize the allocated memory with, or 0 to not initialize it.
size
Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.

Returns

Pointer to allocated char array. This pointer will never be NULL.

function memory_pool::clone_node

Synopsis

xml_node<Ch>* clone_node(const xml_node< Ch > *source, xml_node< Ch > *result=0); ++

Description

Clones an xml_node and its hierarchy of child nodes and attributes. Nodes and attributes are allocated from this memory pool. Names and values are not cloned, they are shared between the clone and the source. Result node can be optionally specified as a second parameter, in which case its contents will be replaced with cloned source node. This is useful when you want to clone entire document.

Parameters

source
Node to clone.
result
Node to put results in, or 0 to automatically allocate result node

Returns

Pointer to cloned node. This pointer will never be NULL.

function memory_pool::clear

Synopsis

void clear(); ++

Description

Clears the pool. This causes memory occupied by nodes allocated by the pool to be freed. Any nodes or strings allocated from the pool will no longer be valid.

function memory_pool::set_allocator

Synopsis

void set_allocator(alloc_func *af, free_func *ff); ++

Description

Sets or resets the user-defined memory allocation functions for the pool. This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. Allocation function must not return invalid pointer on failure. It should either throw, stop the program, or use longjmp() function to pass control to other place of program. If it returns invalid pointer, results are undefined.

++ User defined allocation functions must have the following forms:

++void *allocate(std::size_t size);
++void free(void *pointer);

Parameters

af
Allocation function, or 0 to restore default function
ff
Free function, or 0 to restore default function

class rapidxml::parse_error

++ ++ Defined in rapidxml.hpp

Description

Parse error exception. This exception is thrown by the parser when an error occurs. Use what() function to get human-readable error message. Use where() function to get a pointer to position within source text where error was detected.

++ If throwing exceptions by the parser is undesirable, it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. This function must be defined by the user.

++ This class derives from std::exception class.

++ constructor ++ parse_error::parse_error

Synopsis

parse_error(const char *what, void *where); ++

Description

Constructs parse error.

function parse_error::what

Synopsis

virtual const char* what() const; ++

Description

Gets human readable description of error.

Returns

Pointer to null terminated description of the error.

function parse_error::where

Synopsis

Ch* where() const; ++

Description

Gets pointer to character data where error happened. Ch should be the same as char type of xml_document that produced the error.

Returns

Pointer to location within the parsed string where error occured.

class ++ template ++ rapidxml::xml_attribute

++ ++ Defined in rapidxml.hpp
++ Inherits from ++ xml_base

Description

Class representing attribute node of XML document. Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). Note that after parse, both name and value of attribute will point to interior of source text used for parsing. Thus, this text must persist in memory for the lifetime of attribute.

Parameters

Ch
Character type to use.

++ constructor ++ xml_attribute::xml_attribute

Synopsis

xml_attribute(); ++

Description

Constructs an empty attribute with the specified type. Consider using memory_pool of appropriate xml_document if allocating attributes manually.

function xml_attribute::document

Synopsis

xml_document<Ch>* document() const; ++

Description

Gets document of which attribute is a child.

Returns

Pointer to document that contains this attribute, or 0 if there is no parent document.

function xml_attribute::previous_attribute

Synopsis

xml_attribute<Ch>* previous_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets previous attribute, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_attribute::next_attribute

Synopsis

xml_attribute<Ch>* next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets next attribute, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

class ++ template ++ rapidxml::xml_base

++ ++ Defined in rapidxml.hpp
++ Base class for ++ xml_attribute xml_node

Description

Base class for xml_node and xml_attribute implementing common functions: name(), name_size(), value(), value_size() and parent().

Parameters

Ch
Character type to use

++ constructor ++ xml_base::xml_base

Synopsis

xml_base(); ++

function xml_base::name

Synopsis

Ch* name() const; ++

Description

Gets name of the node. Interpretation of name depends on type of node. Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

++ Use name_size() function to determine length of the name.

Returns

Name of node, or empty string if node has no name.

function xml_base::name_size

Synopsis

std::size_t name_size() const; ++

Description

Gets size of node name, not including terminator character. This function works correctly irrespective of whether name is or is not zero terminated.

Returns

Size of node name, in characters.

function xml_base::value

Synopsis

Ch* value() const; ++

Description

Gets value of node. Interpretation of value depends on type of node. Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

++ Use value_size() function to determine length of the value.

Returns

Value of node, or empty string if node has no value.

function xml_base::value_size

Synopsis

std::size_t value_size() const; ++

Description

Gets size of node value, not including terminator character. This function works correctly irrespective of whether value is or is not zero terminated.

Returns

Size of node value, in characters.

function xml_base::name

Synopsis

void name(const Ch *name, std::size_t size); ++

Description

Sets name of node to a non zero-terminated string. See Ownership Of Strings .

++ Note that node does not own its name or value, it only stores a pointer to it. It will not delete or otherwise free the pointer on destruction. It is reponsibility of the user to properly manage lifetime of the string. The easiest way to achieve it is to use memory_pool of the document to allocate the string - on destruction of the document the string will be automatically freed.

++ Size of name must be specified separately, because name does not have to be zero terminated. Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).

Parameters

name
Name of node to set. Does not have to be zero terminated.
size
Size of name, in characters. This does not include zero terminator, if one is present.

function xml_base::name

Synopsis

void name(const Ch *name); ++

Description

Sets name of node to a zero-terminated string. See also Ownership Of Strings and xml_node::name(const Ch *, std::size_t).

Parameters

name
Name of node to set. Must be zero terminated.

function xml_base::value

Synopsis

void value(const Ch *value, std::size_t size); ++

Description

Sets value of node to a non zero-terminated string. See Ownership Of Strings .

++ Note that node does not own its name or value, it only stores a pointer to it. It will not delete or otherwise free the pointer on destruction. It is reponsibility of the user to properly manage lifetime of the string. The easiest way to achieve it is to use memory_pool of the document to allocate the string - on destruction of the document the string will be automatically freed.

++ Size of value must be specified separately, because it does not have to be zero terminated. Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).

++ If an element has a child node of type node_data, it will take precedence over element value when printing. If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.

Parameters

value
value of node to set. Does not have to be zero terminated.
size
Size of value, in characters. This does not include zero terminator, if one is present.

function xml_base::value

Synopsis

void value(const Ch *value); ++

Description

Sets value of node to a zero-terminated string. See also Ownership Of Strings and xml_node::value(const Ch *, std::size_t).

Parameters

value
Vame of node to set. Must be zero terminated.

function xml_base::parent

Synopsis

xml_node<Ch>* parent() const; ++

Description

Gets node parent.

Returns

Pointer to parent node, or 0 if there is no parent.

class ++ template ++ rapidxml::xml_document

++ ++ Defined in rapidxml.hpp
++ Inherits from ++ xml_node memory_pool

Description

This class represents root of the DOM hierarchy. It is also an xml_node and a memory_pool through public inheritance. Use parse() function to build a DOM tree from a zero-terminated XML text string. parse() function allocates memory for nodes and attributes by using functions of xml_document, which are inherited from memory_pool. To access root node of the document, use the document itself, as if it was an xml_node.

Parameters

Ch
Character type to use.

++ constructor ++ xml_document::xml_document

Synopsis

xml_document(); ++

Description

Constructs empty XML document.

function xml_document::parse

Synopsis

void parse(Ch *text); ++

Description

Parses zero-terminated XML string according to given flags. Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. The string must persist for the lifetime of the document. In case of error, rapidxml::parse_error exception will be thrown.

++ If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. Make sure that data is zero-terminated.

++ Document can be parsed into multiple times. Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool.

Parameters

text
XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser.

function xml_document::clear

Synopsis

void clear(); ++

Description

Clears the document by deleting all nodes and clearing the memory pool. All nodes owned by document pool are destroyed.

class ++ template ++ rapidxml::xml_node

++ ++ Defined in rapidxml.hpp
++ Inherits from ++ xml_base
++ Base class for ++ xml_document

Description

Class representing a node of XML document. Each node may have associated name and value strings, which are available through name() and value() functions. Interpretation of name and value depends on type of the node. Type of node can be determined by using type() function.

++ Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. Thus, this text must persist in the memory for the lifetime of node.

Parameters

Ch
Character type to use.

++ constructor ++ xml_node::xml_node

Synopsis

xml_node(node_type type); ++

Description

Constructs an empty node with the specified type. Consider using memory_pool of appropriate document to allocate nodes manually.

Parameters

type
Type of node to construct.

function xml_node::type

Synopsis

node_type type() const; ++

Description

Gets type of node.

Returns

Type of node.

function xml_node::document

Synopsis

xml_document<Ch>* document() const; ++

Description

Gets document of which node is a child.

Returns

Pointer to document that contains this node, or 0 if there is no parent document.

function xml_node::first_node

Synopsis

xml_node<Ch>* first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets first child node, optionally matching node name.

Parameters

name
Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found child, or 0 if not found.

function xml_node::last_node

Synopsis

xml_node<Ch>* last_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets last child node, optionally matching node name. Behaviour is undefined if node has no children. Use first_node() to test if node has children.

Parameters

name
Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found child, or 0 if not found.

function xml_node::previous_sibling

Synopsis

xml_node<Ch>* previous_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets previous sibling node, optionally matching node name. Behaviour is undefined if node has no parent. Use parent() to test if node has a parent.

Parameters

name
Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found sibling, or 0 if not found.

function xml_node::next_sibling

Synopsis

xml_node<Ch>* next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets next sibling node, optionally matching node name. Behaviour is undefined if node has no parent. Use parent() to test if node has a parent.

Parameters

name
Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found sibling, or 0 if not found.

function xml_node::first_attribute

Synopsis

xml_attribute<Ch>* first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets first attribute of node, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_node::last_attribute

Synopsis

xml_attribute<Ch>* last_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; ++

Description

Gets last attribute of node, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_node::type

Synopsis

void type(node_type type); ++

Description

Sets type of node.

Parameters

type
Type of node to set.

function xml_node::prepend_node

Synopsis

void prepend_node(xml_node< Ch > *child); ++

Description

Prepends a new child node. The prepended child becomes the first child, and all existing children are moved one position back.

Parameters

child
Node to prepend.

function xml_node::append_node

Synopsis

void append_node(xml_node< Ch > *child); ++

Description

Appends a new child node. The appended child becomes the last child.

Parameters

child
Node to append.

function xml_node::insert_node

Synopsis

void insert_node(xml_node< Ch > *where, xml_node< Ch > *child); ++

Description

Inserts a new child node at specified place inside the node. All children after and including the specified node are moved one position back.

Parameters

where
Place where to insert the child, or 0 to insert at the back.
child
Node to insert.

function xml_node::remove_first_node

Synopsis

void remove_first_node(); ++

Description

Removes first child node. If node has no children, behaviour is undefined. Use first_node() to test if node has children.

function xml_node::remove_last_node

Synopsis

void remove_last_node(); ++

Description

Removes last child of the node. If node has no children, behaviour is undefined. Use first_node() to test if node has children.

function xml_node::remove_node

Synopsis

void remove_node(xml_node< Ch > *where); ++

Description

Removes specified child from the node.

function xml_node::remove_all_nodes

Synopsis

void remove_all_nodes(); ++

Description

Removes all child nodes (but not attributes).

function xml_node::prepend_attribute

Synopsis

void prepend_attribute(xml_attribute< Ch > *attribute); ++

Description

Prepends a new attribute to the node.

Parameters

attribute
Attribute to prepend.

function xml_node::append_attribute

Synopsis

void append_attribute(xml_attribute< Ch > *attribute); ++

Description

Appends a new attribute to the node.

Parameters

attribute
Attribute to append.

function xml_node::insert_attribute

Synopsis

void insert_attribute(xml_attribute< Ch > *where, xml_attribute< Ch > *attribute); ++

Description

Inserts a new attribute at specified place inside the node. All attributes after and including the specified attribute are moved one position back.

Parameters

where
Place where to insert the attribute, or 0 to insert at the back.
attribute
Attribute to insert.

function xml_node::remove_first_attribute

Synopsis

void remove_first_attribute(); ++

Description

Removes first attribute of the node. If node has no attributes, behaviour is undefined. Use first_attribute() to test if node has attributes.

function xml_node::remove_last_attribute

Synopsis

void remove_last_attribute(); ++

Description

Removes last attribute of the node. If node has no attributes, behaviour is undefined. Use first_attribute() to test if node has attributes.

function xml_node::remove_attribute

Synopsis

void remove_attribute(xml_attribute< Ch > *where); ++

Description

Removes specified attribute from node.

Parameters

where
Pointer to attribute to be removed.

function xml_node::remove_all_attributes

Synopsis

void remove_all_attributes(); ++

Description

Removes all attributes of node.

enum node_type

Description

Enumeration listing all node types produced by the parser. Use xml_node::type() function to query node type.

Values

node_document
A document node. Name and value are empty.
node_element
An element node. Name contains element name. Value contains text of first data node.
node_data
A data node. Name is empty. Value contains data text.
node_cdata
A CDATA node. Name is empty. Value contains data text.
node_comment
A comment node. Name is empty. Value contains comment text.
node_declaration
A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
node_doctype
A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
node_pi
A PI node. Name contains target. Value contains instructions.

function parse_error_handler

Synopsis

void rapidxml::parse_error_handler(const char *what, void *where); ++

Description

When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function is called to notify user about the error. It must be defined by the user.

++ This function cannot return. If it does, the results are undefined.

++ A very simple definition might look like that: ++ void rapidxml::parse_error_handler(const char *what, void *where) ++ { ++ std::cout << "Parse error: " << what << "\n"; ++ std::abort(); ++ } ++

Parameters

what
Human readable description of the error.
where
Pointer to character data where error was detected.

function print

Synopsis

OutIt rapidxml::print(OutIt out, const xml_node< Ch > &node, int flags=0); ++

Description

Prints XML to given output iterator.

Parameters

out
Output iterator to print to.
node
Node to be printed. Pass xml_document to print entire document.
flags
Flags controlling how XML is printed.

Returns

Output iterator pointing to position immediately after last character of printed text.

function print

Synopsis

std::basic_ostream<Ch>& rapidxml::print(std::basic_ostream< Ch > &out, const xml_node< Ch > &node, int flags=0); ++

Description

Prints XML to given output stream.

Parameters

out
Output stream to print to.
node
Node to be printed. Pass xml_document to print entire document.
flags
Flags controlling how XML is printed.

Returns

Output stream.

function operator<<

Synopsis

std::basic_ostream<Ch>& rapidxml::operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node); ++

Description

Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.

Parameters

out
Output stream to print to.
node
Node to be printed.

Returns

Output stream.

++ constant ++ parse_no_data_nodes

Synopsis

const int parse_no_data_nodes ++ = 0x1; ++

Description

Parse flag instructing the parser to not create data nodes. Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_no_element_values

Synopsis

const int parse_no_element_values ++ = 0x2; ++

Description

Parse flag instructing the parser to not use text of first data node as a value of parent element. Can be combined with other flags by use of | operator. Note that child data nodes of element node take precendence over its value when printing. That is, if element has one or more child data nodes and a value, the value will be ignored. Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.

++ See xml_document::parse() function.

++ constant ++ parse_no_string_terminators

Synopsis

const int parse_no_string_terminators ++ = 0x4; ++

Description

Parse flag instructing the parser to not place zero terminators after strings in the source text. By default zero terminators are placed, modifying source text. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_no_entity_translation

Synopsis

const int parse_no_entity_translation ++ = 0x8; ++

Description

Parse flag instructing the parser to not translate entities in the source text. By default entities are translated, modifying source text. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_no_utf8

Synopsis

const int parse_no_utf8 ++ = 0x10; ++

Description

Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. By default, UTF-8 handling is enabled. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_declaration_node

Synopsis

const int parse_declaration_node ++ = 0x20; ++

Description

Parse flag instructing the parser to create XML declaration node. By default, declaration node is not created. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_comment_nodes

Synopsis

const int parse_comment_nodes ++ = 0x40; ++

Description

Parse flag instructing the parser to create comments nodes. By default, comment nodes are not created. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_doctype_node

Synopsis

const int parse_doctype_node ++ = 0x80; ++

Description

Parse flag instructing the parser to create DOCTYPE node. By default, doctype node is not created. Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_pi_nodes

Synopsis

const int parse_pi_nodes ++ = 0x100; ++

Description

Parse flag instructing the parser to create PI nodes. By default, PI nodes are not created. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_validate_closing_tags

Synopsis

const int parse_validate_closing_tags ++ = 0x200; ++

Description

Parse flag instructing the parser to validate closing tag names. If not set, name inside closing tag is irrelevant to the parser. By default, closing tags are not validated. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_trim_whitespace

Synopsis

const int parse_trim_whitespace ++ = 0x400; ++

Description

Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. By default, whitespace is not trimmed. This flag does not cause the parser to modify source text. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_normalize_whitespace

Synopsis

const int parse_normalize_whitespace ++ = 0x800; ++

Description

Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. By default, whitespace is not normalized. If this flag is specified, source text will be modified. Can be combined with other flags by use of | operator.

++ See xml_document::parse() function.

++ constant ++ parse_default

Synopsis

const int parse_default ++ = 0; ++

Description

Parse flags which represent default behaviour of the parser. This is always equal to 0, so that all other flags can be simply ored together. Normally there is no need to inconveniently disable flags by anding with their negated (~) values. This also means that meaning of each flag is a negation of the default setting. For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, and using the flag will disable it.

++ See xml_document::parse() function.

++ constant ++ parse_non_destructive

Synopsis

const int parse_non_destructive ++ = parse_no_string_terminators | parse_no_entity_translation; ++

Description

A combination of parse flags that forbids any modifications of the source text. This also results in faster parsing. However, note that the following will occur:
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • entities will not be translated
  • whitespace will not be normalized
++See xml_document::parse() function.

++ constant ++ parse_fastest

Synopsis

const int parse_fastest ++ = parse_non_destructive | parse_no_data_nodes; ++

Description

A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.

++ See xml_document::parse() function.

++ constant ++ parse_full

Synopsis

const int parse_full ++ = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; ++

Description

A combination of parse flags resulting in largest amount of data being extracted. This usually results in slowest parsing.

++ See xml_document::parse() function.

++ constant ++ print_no_indenting

Synopsis

const int print_no_indenting ++ = 0x1; ++

Description

Printer flag instructing the printer to suppress indenting of XML. See print() function.

+\ No newline at end of file +diff --git a/lib/rapidxml/rapidxml.hpp b/lib/rapidxml/rapidxml.hpp +new file mode 100644 +index 0000000..fb24a63 +--- /dev/null ++++ b/lib/rapidxml/rapidxml.hpp +@@ -0,0 +1,2596 @@ ++#ifndef RAPIDXML_HPP_INCLUDED ++#define RAPIDXML_HPP_INCLUDED ++ ++// Copyright (C) 2006, 2009 Marcin Kalicinski ++// Version 1.13 ++// Revision $DateTime: 2009/05/13 01:46:17 $ ++//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation ++ ++// If standard library is disabled, user must provide implementations of required functions and typedefs ++//#if !defined(RAPIDXML_NO_STDLIB) ++ #include // For std::size_t ++ #include // For assert ++ #include // For placement new ++//#endif ++ ++// On MSVC, disable "conditional expression is constant" warning (level 4). ++// This warning is almost impossible to avoid with certain types of templated code ++#ifdef _MSC_VER ++ #pragma warning(push) ++ #pragma warning(disable:4127) // Conditional expression is constant ++#endif ++ ++/////////////////////////////////////////////////////////////////////////// ++// RAPIDXML_PARSE_ERROR ++ ++#if defined(RAPIDXML_NO_EXCEPTIONS) ++ ++#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } ++ ++namespace rapidxml ++{ ++ //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, ++ //! this function is called to notify user about the error. ++ //! It must be defined by the user. ++ //!

++ //! This function cannot return. If it does, the results are undefined. ++ //!

++ //! A very simple definition might look like that: ++ //!

++    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
++    //! {
++    //!     std::cout << "Parse error: " << what << "\n";
++    //!     std::abort();
++    //! }
++    //! 
++ //! \param what Human readable description of the error. ++ //! \param where Pointer to character data where error was detected. ++ void parse_error_handler(const char *what, void *where); ++} ++ ++#else ++ ++#include // For std::exception ++ ++#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) ++ ++namespace rapidxml ++{ ++ ++ //! Parse error exception. ++ //! This exception is thrown by the parser when an error occurs. ++ //! Use what() function to get human-readable error message. ++ //! Use where() function to get a pointer to position within source text where error was detected. ++ //!

++ //! If throwing exceptions by the parser is undesirable, ++ //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. ++ //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. ++ //! This function must be defined by the user. ++ //!

++ //! This class derives from std::exception class. ++ class parse_error: public std::exception ++ { ++ ++ public: ++ ++ //! Constructs parse error ++ parse_error(const char *what, void *where) ++ : m_what(what) ++ , m_where(where) ++ { ++ } ++ ++ //! Gets human readable description of error. ++ //! \return Pointer to null terminated description of the error. ++ virtual const char *what() const throw() ++ { ++ return m_what; ++ } ++ ++ //! Gets pointer to character data where error happened. ++ //! Ch should be the same as char type of xml_document that produced the error. ++ //! \return Pointer to location within the parsed string where error occured. ++ template ++ Ch *where() const ++ { ++ return reinterpret_cast(m_where); ++ } ++ ++ private: ++ ++ const char *m_what; ++ void *m_where; ++ ++ }; ++} ++ ++#endif ++ ++/////////////////////////////////////////////////////////////////////////// ++// Pool sizes ++ ++#ifndef RAPIDXML_STATIC_POOL_SIZE ++ // Size of static memory block of memory_pool. ++ // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. ++ // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. ++ #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) ++#endif ++ ++#ifndef RAPIDXML_DYNAMIC_POOL_SIZE ++ // Size of dynamic memory block of memory_pool. ++ // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. ++ // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. ++ #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) ++#endif ++ ++#ifndef RAPIDXML_ALIGNMENT ++ // Memory allocation alignment. ++ // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. ++ // All memory allocations for nodes, attributes and strings will be aligned to this value. ++ // This must be a power of 2 and at least 1, otherwise memory_pool will not work. ++ #define RAPIDXML_ALIGNMENT sizeof(void *) ++#endif ++ ++namespace rapidxml ++{ ++ // Forward declarations ++ template class xml_node; ++ template class xml_attribute; ++ template class xml_document; ++ ++ //! Enumeration listing all node types produced by the parser. ++ //! Use xml_node::type() function to query node type. ++ enum node_type ++ { ++ node_document, //!< A document node. Name and value are empty. ++ node_element, //!< An element node. Name contains element name. Value contains text of first data node. ++ node_data, //!< A data node. Name is empty. Value contains data text. ++ node_cdata, //!< A CDATA node. Name is empty. Value contains data text. ++ node_comment, //!< A comment node. Name is empty. Value contains comment text. ++ node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. ++ node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. ++ node_pi //!< A PI node. Name contains target. Value contains instructions. ++ }; ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Parsing flags ++ ++ //! Parse flag instructing the parser to not create data nodes. ++ //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_no_data_nodes = 0x1; ++ ++ //! Parse flag instructing the parser to not use text of first data node as a value of parent element. ++ //! Can be combined with other flags by use of | operator. ++ //! Note that child data nodes of element node take precendence over its value when printing. ++ //! That is, if element has one or more child data nodes and a value, the value will be ignored. ++ //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_no_element_values = 0x2; ++ ++ //! Parse flag instructing the parser to not place zero terminators after strings in the source text. ++ //! By default zero terminators are placed, modifying source text. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_no_string_terminators = 0x4; ++ ++ //! Parse flag instructing the parser to not translate entities in the source text. ++ //! By default entities are translated, modifying source text. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_no_entity_translation = 0x8; ++ ++ //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. ++ //! By default, UTF-8 handling is enabled. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_no_utf8 = 0x10; ++ ++ //! Parse flag instructing the parser to create XML declaration node. ++ //! By default, declaration node is not created. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_declaration_node = 0x20; ++ ++ //! Parse flag instructing the parser to create comments nodes. ++ //! By default, comment nodes are not created. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_comment_nodes = 0x40; ++ ++ //! Parse flag instructing the parser to create DOCTYPE node. ++ //! By default, doctype node is not created. ++ //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_doctype_node = 0x80; ++ ++ //! Parse flag instructing the parser to create PI nodes. ++ //! By default, PI nodes are not created. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_pi_nodes = 0x100; ++ ++ //! Parse flag instructing the parser to validate closing tag names. ++ //! If not set, name inside closing tag is irrelevant to the parser. ++ //! By default, closing tags are not validated. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_validate_closing_tags = 0x200; ++ ++ //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. ++ //! By default, whitespace is not trimmed. ++ //! This flag does not cause the parser to modify source text. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_trim_whitespace = 0x400; ++ ++ //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. ++ //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. ++ //! By default, whitespace is not normalized. ++ //! If this flag is specified, source text will be modified. ++ //! Can be combined with other flags by use of | operator. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_normalize_whitespace = 0x800; ++ ++ // Compound flags ++ ++ //! Parse flags which represent default behaviour of the parser. ++ //! This is always equal to 0, so that all other flags can be simply ored together. ++ //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. ++ //! This also means that meaning of each flag is a negation of the default setting. ++ //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, ++ //! and using the flag will disable it. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_default = 0; ++ ++ //! A combination of parse flags that forbids any modifications of the source text. ++ //! This also results in faster parsing. However, note that the following will occur: ++ //!
    ++ //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • ++ //!
  • entities will not be translated
  • ++ //!
  • whitespace will not be normalized
  • ++ //!
++ //! See xml_document::parse() function. ++ const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; ++ ++ //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_fastest = parse_non_destructive | parse_no_data_nodes; ++ ++ //! A combination of parse flags resulting in largest amount of data being extracted. ++ //! This usually results in slowest parsing. ++ //!

++ //! See xml_document::parse() function. ++ const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Internals ++ ++ //! \cond internal ++ namespace internal ++ { ++ ++ // Struct that contains lookup tables for the parser ++ // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). ++ template ++ struct lookup_tables ++ { ++ static const unsigned char lookup_whitespace[256]; // Whitespace table ++ static const unsigned char lookup_node_name[256]; // Node name table ++ static const unsigned char lookup_text[256]; // Text table ++ static const unsigned char lookup_text_pure_no_ws[256]; // Text table ++ static const unsigned char lookup_text_pure_with_ws[256]; // Text table ++ static const unsigned char lookup_attribute_name[256]; // Attribute name table ++ static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote ++ static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote ++ static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes ++ static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes ++ static const unsigned char lookup_digits[256]; // Digits ++ static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters ++ }; ++ ++ // Find length of the string ++ template ++ inline std::size_t measure(const Ch *p) ++ { ++ const Ch *tmp = p; ++ while (*tmp) ++ ++tmp; ++ return tmp - p; ++ } ++ ++ // Compare strings for equality ++ template ++ inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) ++ { ++ if (size1 != size2) ++ return false; ++ if (case_sensitive) ++ { ++ for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) ++ if (*p1 != *p2) ++ return false; ++ } ++ else ++ { ++ for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) ++ if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) ++ return false; ++ } ++ return true; ++ } ++ } ++ //! \endcond ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Memory pool ++ ++ //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. ++ //! In most cases, you will not need to use this class directly. ++ //! However, if you need to create nodes manually or modify names/values of nodes, ++ //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. ++ //! Not only is this faster than allocating them by using new operator, ++ //! but also their lifetime will be tied to the lifetime of document, ++ //! possibly simplyfing memory management. ++ //!

++ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. ++ //! You can also call allocate_string() function to allocate strings. ++ //! Such strings can then be used as names or values of nodes without worrying about their lifetime. ++ //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, ++ //! or when the pool is destroyed. ++ //!

++ //! It is also possible to create a standalone memory_pool, and use it ++ //! to allocate nodes, whose lifetime will not be tied to any document. ++ //!

++ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. ++ //! Until static memory is exhausted, no dynamic memory allocations are done. ++ //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, ++ //! by using global new[] and delete[] operators. ++ //! This behaviour can be changed by setting custom allocation routines. ++ //! Use set_allocator() function to set them. ++ //!

++ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. ++ //! This value defaults to the size of pointer on target architecture. ++ //!

++ //! To obtain absolutely top performance from the parser, ++ //! it is important that all nodes are allocated from a single, contiguous block of memory. ++ //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. ++ //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT ++ //! to obtain best wasted memory to performance compromise. ++ //! To do it, define their values before rapidxml.hpp file is included. ++ //! \param Ch Character type of created nodes. ++ template ++ class memory_pool ++ { ++ ++ public: ++ ++ //! \cond internal ++ typedef void *(alloc_func1)(std::size_t); // Type of user-defined function used to allocate memory ++ typedef void (free_func)(void *); // Type of user-defined function used to free memory ++ //! \endcond ++ ++ //! Constructs empty pool with default allocator functions. ++ memory_pool() ++ : m_alloc_func(0) ++ , m_free_func(0) ++ { ++ init(); ++ } ++ ++ //! Destroys pool and frees all the memory. ++ //! This causes memory occupied by nodes allocated by the pool to be freed. ++ //! Nodes allocated from the pool are no longer valid. ++ ~memory_pool() ++ { ++ clear(); ++ } ++ ++ //! Allocates a new node from the pool, and optionally assigns name and value to it. ++ //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. ++ //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function ++ //! will call rapidxml::parse_error_handler() function. ++ //! \param type Type of node to create. ++ //! \param name Name to assign to the node, or 0 to assign no name. ++ //! \param value Value to assign to the node, or 0 to assign no value. ++ //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. ++ //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. ++ //! \return Pointer to allocated node. This pointer will never be NULL. ++ xml_node *allocate_node(node_type type, ++ const Ch *name = 0, const Ch *value = 0, ++ std::size_t name_size = 0, std::size_t value_size = 0) ++ { ++ void *memory = allocate_aligned(sizeof(xml_node)); ++ xml_node *node = new(memory) xml_node(type); ++ if (name) ++ { ++ if (name_size > 0) ++ node->name(name, name_size); ++ else ++ node->name(name); ++ } ++ if (value) ++ { ++ if (value_size > 0) ++ node->value(value, value_size); ++ else ++ node->value(value); ++ } ++ return node; ++ } ++ ++ //! Allocates a new attribute from the pool, and optionally assigns name and value to it. ++ //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. ++ //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function ++ //! will call rapidxml::parse_error_handler() function. ++ //! \param name Name to assign to the attribute, or 0 to assign no name. ++ //! \param value Value to assign to the attribute, or 0 to assign no value. ++ //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. ++ //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. ++ //! \return Pointer to allocated attribute. This pointer will never be NULL. ++ xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, ++ std::size_t name_size = 0, std::size_t value_size = 0) ++ { ++ void *memory = allocate_aligned(sizeof(xml_attribute)); ++ xml_attribute *attribute = new(memory) xml_attribute; ++ if (name) ++ { ++ if (name_size > 0) ++ attribute->name(name, name_size); ++ else ++ attribute->name(name); ++ } ++ if (value) ++ { ++ if (value_size > 0) ++ attribute->value(value, value_size); ++ else ++ attribute->value(value); ++ } ++ return attribute; ++ } ++ ++ //! Allocates a char array of given size from the pool, and optionally copies a given string to it. ++ //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. ++ //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function ++ //! will call rapidxml::parse_error_handler() function. ++ //! \param source String to initialize the allocated memory with, or 0 to not initialize it. ++ //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. ++ //! \return Pointer to allocated char array. This pointer will never be NULL. ++ Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) ++ { ++ assert(source || size); // Either source or size (or both) must be specified ++ if (size == 0) ++ size = internal::measure(source) + 1; ++ Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); ++ if (source) ++ for (std::size_t i = 0; i < size; ++i) ++ result[i] = source[i]; ++ return result; ++ } ++ ++ //! Clones an xml_node and its hierarchy of child nodes and attributes. ++ //! Nodes and attributes are allocated from this memory pool. ++ //! Names and values are not cloned, they are shared between the clone and the source. ++ //! Result node can be optionally specified as a second parameter, ++ //! in which case its contents will be replaced with cloned source node. ++ //! This is useful when you want to clone entire document. ++ //! \param source Node to clone. ++ //! \param result Node to put results in, or 0 to automatically allocate result node ++ //! \return Pointer to cloned node. This pointer will never be NULL. ++ xml_node *clone_node(const xml_node *source, xml_node *result = 0) ++ { ++ // Prepare result node ++ if (result) ++ { ++ result->remove_all_attributes(); ++ result->remove_all_nodes(); ++ result->type(source->type()); ++ } ++ else ++ result = allocate_node(source->type()); ++ ++ // Clone name and value ++ result->name(source->name(), source->name_size()); ++ result->value(source->value(), source->value_size()); ++ ++ // Clone child nodes and attributes ++ for (xml_node *child = source->first_node(); child; child = child->next_sibling()) ++ result->append_node(clone_node(child)); ++ for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) ++ result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); ++ ++ return result; ++ } ++ ++ //! Clears the pool. ++ //! This causes memory occupied by nodes allocated by the pool to be freed. ++ //! Any nodes or strings allocated from the pool will no longer be valid. ++ void clear() ++ { ++ while (m_begin != m_static_memory) ++ { ++ char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; ++ if (m_free_func) ++ m_free_func(m_begin); ++ else ++ delete[] m_begin; ++ m_begin = previous_begin; ++ } ++ init(); ++ } ++ ++ //! Sets or resets the user-defined memory allocation functions for the pool. ++ //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. ++ //! Allocation function must not return invalid pointer on failure. It should either throw, ++ //! stop the program, or use longjmp() function to pass control to other place of program. ++ //! If it returns invalid pointer, results are undefined. ++ //!

++ //! User defined allocation functions must have the following forms: ++ //!
++ //!
void *allocate(std::size_t size); ++ //!
void free(void *pointer); ++ //!

++ //! \param af Allocation function, or 0 to restore default function ++ //! \param ff Free function, or 0 to restore default function ++ void set_allocator(alloc_func1 *af, free_func *ff) ++ { ++ assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet ++ m_alloc_func = af; ++ m_free_func = ff; ++ } ++ ++ private: ++ ++ struct header ++ { ++ char *previous_begin; ++ }; ++ ++ void init() ++ { ++ m_begin = m_static_memory; ++ m_ptr = align(m_begin); ++ m_end = m_static_memory + sizeof(m_static_memory); ++ } ++ ++ char *align(char *ptr) ++ { ++ std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); ++ return ptr + alignment; ++ } ++ ++ char *allocate_raw(std::size_t size) ++ { ++ // Allocate ++ void *memory; ++ if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] ++ { ++ memory = m_alloc_func(size); ++ assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp ++ } ++ else ++ { ++ memory = new char[size]; ++#ifdef RAPIDXML_NO_EXCEPTIONS ++ if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc ++ RAPIDXML_PARSE_ERROR("out of memory", 0); ++#endif ++ } ++ return static_cast(memory); ++ } ++ ++ void *allocate_aligned(std::size_t size) ++ { ++ // Calculate aligned pointer ++ char *result = align(m_ptr); ++ ++ // If not enough memory left in current pool, allocate a new pool ++ if (result + size > m_end) ++ { ++ // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) ++ std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; ++ if (pool_size < size) ++ pool_size = size; ++ ++ // Allocate ++ std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation ++ char *raw_memory = allocate_raw(alloc_size); ++ ++ // Setup new pool in allocated memory ++ char *pool = align(raw_memory); ++ header *new_header = reinterpret_cast
(pool); ++ new_header->previous_begin = m_begin; ++ m_begin = raw_memory; ++ m_ptr = pool + sizeof(header); ++ m_end = raw_memory + alloc_size; ++ ++ // Calculate aligned pointer again using new pool ++ result = align(m_ptr); ++ } ++ ++ // Update pool and return aligned pointer ++ m_ptr = result + size; ++ return result; ++ } ++ ++ char *m_begin; // Start of raw memory making up current pool ++ char *m_ptr; // First free byte in current pool ++ char *m_end; // One past last available byte in current pool ++ char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory ++ alloc_func1 *m_alloc_func; // Allocator function, or 0 if default is to be used ++ free_func *m_free_func; // Free function, or 0 if default is to be used ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // XML base ++ ++ //! Base class for xml_node and xml_attribute implementing common functions: ++ //! name(), name_size(), value(), value_size() and parent(). ++ //! \param Ch Character type to use ++ template ++ class xml_base ++ { ++ ++ public: ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Construction & destruction ++ ++ // Construct a base with empty name, value and parent ++ xml_base() ++ : m_name(0) ++ , m_value(0) ++ , m_parent(0) ++ { ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Node data access ++ ++ //! Gets name of the node. ++ //! Interpretation of name depends on type of node. ++ //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. ++ //!

++ //! Use name_size() function to determine length of the name. ++ //! \return Name of node, or empty string if node has no name. ++ Ch *name() const ++ { ++ return m_name ? m_name : nullstr(); ++ } ++ ++ //! Gets size of node name, not including terminator character. ++ //! This function works correctly irrespective of whether name is or is not zero terminated. ++ //! \return Size of node name, in characters. ++ std::size_t name_size() const ++ { ++ return m_name ? m_name_size : 0; ++ } ++ ++ //! Gets value of node. ++ //! Interpretation of value depends on type of node. ++ //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. ++ //!

++ //! Use value_size() function to determine length of the value. ++ //! \return Value of node, or empty string if node has no value. ++ Ch *value() const ++ { ++ return m_value ? m_value : nullstr(); ++ } ++ ++ //! Gets size of node value, not including terminator character. ++ //! This function works correctly irrespective of whether value is or is not zero terminated. ++ //! \return Size of node value, in characters. ++ std::size_t value_size() const ++ { ++ return m_value ? m_value_size : 0; ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Node modification ++ ++ //! Sets name of node to a non zero-terminated string. ++ //! See \ref ownership_of_strings. ++ //!

++ //! Note that node does not own its name or value, it only stores a pointer to it. ++ //! It will not delete or otherwise free the pointer on destruction. ++ //! It is reponsibility of the user to properly manage lifetime of the string. ++ //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - ++ //! on destruction of the document the string will be automatically freed. ++ //!

++ //! Size of name must be specified separately, because name does not have to be zero terminated. ++ //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). ++ //! \param name Name of node to set. Does not have to be zero terminated. ++ //! \param size Size of name, in characters. This does not include zero terminator, if one is present. ++ void name(const Ch *name, std::size_t size) ++ { ++ m_name = const_cast(name); ++ m_name_size = size; ++ } ++ ++ //! Sets name of node to a zero-terminated string. ++ //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). ++ //! \param name Name of node to set. Must be zero terminated. ++ void name(const Ch *name) ++ { ++ this->name(name, internal::measure(name)); ++ } ++ ++ //! Sets value of node to a non zero-terminated string. ++ //! See \ref ownership_of_strings. ++ //!

++ //! Note that node does not own its name or value, it only stores a pointer to it. ++ //! It will not delete or otherwise free the pointer on destruction. ++ //! It is reponsibility of the user to properly manage lifetime of the string. ++ //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - ++ //! on destruction of the document the string will be automatically freed. ++ //!

++ //! Size of value must be specified separately, because it does not have to be zero terminated. ++ //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). ++ //!

++ //! If an element has a child node of type node_data, it will take precedence over element value when printing. ++ //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. ++ //! \param value value of node to set. Does not have to be zero terminated. ++ //! \param size Size of value, in characters. This does not include zero terminator, if one is present. ++ void value(const Ch *value, std::size_t size) ++ { ++ m_value = const_cast(value); ++ m_value_size = size; ++ } ++ ++ //! Sets value of node to a zero-terminated string. ++ //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). ++ //! \param value Vame of node to set. Must be zero terminated. ++ void value(const Ch *value) ++ { ++ this->value(value, internal::measure(value)); ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Related nodes access ++ ++ //! Gets node parent. ++ //! \return Pointer to parent node, or 0 if there is no parent. ++ xml_node *parent() const ++ { ++ return m_parent; ++ } ++ ++ protected: ++ ++ // Return empty string ++ static Ch *nullstr() ++ { ++ static Ch zero = Ch('\0'); ++ return &zero; ++ } ++ ++ Ch *m_name; // Name of node, or 0 if no name ++ Ch *m_value; // Value of node, or 0 if no value ++ std::size_t m_name_size; // Length of node name, or undefined of no name ++ std::size_t m_value_size; // Length of node value, or undefined if no value ++ xml_node *m_parent; // Pointer to parent node, or 0 if none ++ ++ }; ++ ++ //! Class representing attribute node of XML document. ++ //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). ++ //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. ++ //! Thus, this text must persist in memory for the lifetime of attribute. ++ //! \param Ch Character type to use. ++ template ++ class xml_attribute: public xml_base ++ { ++ ++ friend class xml_node; ++ ++ public: ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Construction & destruction ++ ++ //! Constructs an empty attribute with the specified type. ++ //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. ++ xml_attribute() ++ { ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Related nodes access ++ ++ //! Gets document of which attribute is a child. ++ //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. ++ xml_document *document() const ++ { ++ if (xml_node *node = this->parent()) ++ { ++ while (node->parent()) ++ node = node->parent(); ++ return node->type() == node_document ? static_cast *>(node) : 0; ++ } ++ else ++ return 0; ++ } ++ ++ //! Gets previous attribute, optionally matching attribute name. ++ //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found attribute, or 0 if not found. ++ xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) ++ if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) ++ return attribute; ++ return 0; ++ } ++ else ++ return this->m_parent ? m_prev_attribute : 0; ++ } ++ ++ //! Gets next attribute, optionally matching attribute name. ++ //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found attribute, or 0 if not found. ++ xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) ++ if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) ++ return attribute; ++ return 0; ++ } ++ else ++ return this->m_parent ? m_next_attribute : 0; ++ } ++ ++ private: ++ ++ xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero ++ xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero ++ ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // XML node ++ ++ //! Class representing a node of XML document. ++ //! Each node may have associated name and value strings, which are available through name() and value() functions. ++ //! Interpretation of name and value depends on type of the node. ++ //! Type of node can be determined by using type() function. ++ //!

++ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. ++ //! Thus, this text must persist in the memory for the lifetime of node. ++ //! \param Ch Character type to use. ++ template ++ class xml_node: public xml_base ++ { ++ ++ public: ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Construction & destruction ++ ++ //! Constructs an empty node with the specified type. ++ //! Consider using memory_pool of appropriate document to allocate nodes manually. ++ //! \param type Type of node to construct. ++ xml_node(node_type type) ++ : m_type(type) ++ , m_first_node(0) ++ , m_first_attribute(0) ++ { ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Node data access ++ ++ //! Gets type of node. ++ //! \return Type of node. ++ node_type type() const ++ { ++ return m_type; ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Related nodes access ++ ++ //! Gets document of which node is a child. ++ //! \return Pointer to document that contains this node, or 0 if there is no parent document. ++ xml_document *document() const ++ { ++ xml_node *node = const_cast *>(this); ++ while (node->parent()) ++ node = node->parent(); ++ return node->type() == node_document ? static_cast *>(node) : 0; ++ } ++ ++ //! Gets first child node, optionally matching node name. ++ //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found child, or 0 if not found. ++ xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_node *child = m_first_node; child; child = child->next_sibling()) ++ if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) ++ return child; ++ return 0; ++ } ++ else ++ return m_first_node; ++ } ++ ++ //! Gets last child node, optionally matching node name. ++ //! Behaviour is undefined if node has no children. ++ //! Use first_node() to test if node has children. ++ //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found child, or 0 if not found. ++ xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ assert(m_first_node); // Cannot query for last child if node has no children ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_node *child = m_last_node; child; child = child->previous_sibling()) ++ if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) ++ return child; ++ return 0; ++ } ++ else ++ return m_last_node; ++ } ++ ++ //! Gets previous sibling node, optionally matching node name. ++ //! Behaviour is undefined if node has no parent. ++ //! Use parent() to test if node has a parent. ++ //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found sibling, or 0 if not found. ++ xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ assert(this->m_parent); // Cannot query for siblings if node has no parent ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) ++ if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) ++ return sibling; ++ return 0; ++ } ++ else ++ return m_prev_sibling; ++ } ++ ++ //! Gets next sibling node, optionally matching node name. ++ //! Behaviour is undefined if node has no parent. ++ //! Use parent() to test if node has a parent. ++ //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found sibling, or 0 if not found. ++ xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ assert(this->m_parent); // Cannot query for siblings if node has no parent ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) ++ if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) ++ return sibling; ++ return 0; ++ } ++ else ++ return m_next_sibling; ++ } ++ ++ //! Gets first attribute of node, optionally matching attribute name. ++ //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found attribute, or 0 if not found. ++ xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) ++ if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) ++ return attribute; ++ return 0; ++ } ++ else ++ return m_first_attribute; ++ } ++ ++ //! Gets last attribute of node, optionally matching attribute name. ++ //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero ++ //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string ++ //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters ++ //! \return Pointer to found attribute, or 0 if not found. ++ xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const ++ { ++ if (name) ++ { ++ if (name_size == 0) ++ name_size = internal::measure(name); ++ for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) ++ if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) ++ return attribute; ++ return 0; ++ } ++ else ++ return m_first_attribute ? m_last_attribute : 0; ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Node modification ++ ++ //! Sets type of node. ++ //! \param type Type of node to set. ++ void type(node_type type) ++ { ++ m_type = type; ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Node manipulation ++ ++ //! Prepends a new child node. ++ //! The prepended child becomes the first child, and all existing children are moved one position back. ++ //! \param child Node to prepend. ++ void prepend_node(xml_node *child) ++ { ++ assert(child && !child->parent() && child->type() != node_document); ++ if (first_node()) ++ { ++ child->m_next_sibling = m_first_node; ++ m_first_node->m_prev_sibling = child; ++ } ++ else ++ { ++ child->m_next_sibling = 0; ++ m_last_node = child; ++ } ++ m_first_node = child; ++ child->m_parent = this; ++ child->m_prev_sibling = 0; ++ } ++ ++ //! Appends a new child node. ++ //! The appended child becomes the last child. ++ //! \param child Node to append. ++ void append_node(xml_node *child) ++ { ++ assert(child && !child->parent() && child->type() != node_document); ++ if (first_node()) ++ { ++ child->m_prev_sibling = m_last_node; ++ m_last_node->m_next_sibling = child; ++ } ++ else ++ { ++ child->m_prev_sibling = 0; ++ m_first_node = child; ++ } ++ m_last_node = child; ++ child->m_parent = this; ++ child->m_next_sibling = 0; ++ } ++ ++ //! Inserts a new child node at specified place inside the node. ++ //! All children after and including the specified node are moved one position back. ++ //! \param where Place where to insert the child, or 0 to insert at the back. ++ //! \param child Node to insert. ++ void insert_node(xml_node *where, xml_node *child) ++ { ++ assert(!where || where->parent() == this); ++ assert(child && !child->parent() && child->type() != node_document); ++ if (where == m_first_node) ++ prepend_node(child); ++ else if (where == 0) ++ append_node(child); ++ else ++ { ++ child->m_prev_sibling = where->m_prev_sibling; ++ child->m_next_sibling = where; ++ where->m_prev_sibling->m_next_sibling = child; ++ where->m_prev_sibling = child; ++ child->m_parent = this; ++ } ++ } ++ ++ //! Removes first child node. ++ //! If node has no children, behaviour is undefined. ++ //! Use first_node() to test if node has children. ++ void remove_first_node() ++ { ++ assert(first_node()); ++ xml_node *child = m_first_node; ++ m_first_node = child->m_next_sibling; ++ if (child->m_next_sibling) ++ child->m_next_sibling->m_prev_sibling = 0; ++ else ++ m_last_node = 0; ++ child->m_parent = 0; ++ } ++ ++ //! Removes last child of the node. ++ //! If node has no children, behaviour is undefined. ++ //! Use first_node() to test if node has children. ++ void remove_last_node() ++ { ++ assert(first_node()); ++ xml_node *child = m_last_node; ++ if (child->m_prev_sibling) ++ { ++ m_last_node = child->m_prev_sibling; ++ child->m_prev_sibling->m_next_sibling = 0; ++ } ++ else ++ m_first_node = 0; ++ child->m_parent = 0; ++ } ++ ++ //! Removes specified child from the node ++ // \param where Pointer to child to be removed. ++ void remove_node(xml_node *where) ++ { ++ assert(where && where->parent() == this); ++ assert(first_node()); ++ if (where == m_first_node) ++ remove_first_node(); ++ else if (where == m_last_node) ++ remove_last_node(); ++ else ++ { ++ where->m_prev_sibling->m_next_sibling = where->m_next_sibling; ++ where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; ++ where->m_parent = 0; ++ } ++ } ++ ++ //! Removes all child nodes (but not attributes). ++ void remove_all_nodes() ++ { ++ for (xml_node *node = first_node(); node; node = node->m_next_sibling) ++ node->m_parent = 0; ++ m_first_node = 0; ++ } ++ ++ //! Prepends a new attribute to the node. ++ //! \param attribute Attribute to prepend. ++ void prepend_attribute(xml_attribute *attribute) ++ { ++ assert(attribute && !attribute->parent()); ++ if (first_attribute()) ++ { ++ attribute->m_next_attribute = m_first_attribute; ++ m_first_attribute->m_prev_attribute = attribute; ++ } ++ else ++ { ++ attribute->m_next_attribute = 0; ++ m_last_attribute = attribute; ++ } ++ m_first_attribute = attribute; ++ attribute->m_parent = this; ++ attribute->m_prev_attribute = 0; ++ } ++ ++ //! Appends a new attribute to the node. ++ //! \param attribute Attribute to append. ++ void append_attribute(xml_attribute *attribute) ++ { ++ assert(attribute && !attribute->parent()); ++ if (first_attribute()) ++ { ++ attribute->m_prev_attribute = m_last_attribute; ++ m_last_attribute->m_next_attribute = attribute; ++ } ++ else ++ { ++ attribute->m_prev_attribute = 0; ++ m_first_attribute = attribute; ++ } ++ m_last_attribute = attribute; ++ attribute->m_parent = this; ++ attribute->m_next_attribute = 0; ++ } ++ ++ //! Inserts a new attribute at specified place inside the node. ++ //! All attributes after and including the specified attribute are moved one position back. ++ //! \param where Place where to insert the attribute, or 0 to insert at the back. ++ //! \param attribute Attribute to insert. ++ void insert_attribute(xml_attribute *where, xml_attribute *attribute) ++ { ++ assert(!where || where->parent() == this); ++ assert(attribute && !attribute->parent()); ++ if (where == m_first_attribute) ++ prepend_attribute(attribute); ++ else if (where == 0) ++ append_attribute(attribute); ++ else ++ { ++ attribute->m_prev_attribute = where->m_prev_attribute; ++ attribute->m_next_attribute = where; ++ where->m_prev_attribute->m_next_attribute = attribute; ++ where->m_prev_attribute = attribute; ++ attribute->m_parent = this; ++ } ++ } ++ ++ //! Removes first attribute of the node. ++ //! If node has no attributes, behaviour is undefined. ++ //! Use first_attribute() to test if node has attributes. ++ void remove_first_attribute() ++ { ++ assert(first_attribute()); ++ xml_attribute *attribute = m_first_attribute; ++ if (attribute->m_next_attribute) ++ { ++ attribute->m_next_attribute->m_prev_attribute = 0; ++ } ++ else ++ m_last_attribute = 0; ++ attribute->m_parent = 0; ++ m_first_attribute = attribute->m_next_attribute; ++ } ++ ++ //! Removes last attribute of the node. ++ //! If node has no attributes, behaviour is undefined. ++ //! Use first_attribute() to test if node has attributes. ++ void remove_last_attribute() ++ { ++ assert(first_attribute()); ++ xml_attribute *attribute = m_last_attribute; ++ if (attribute->m_prev_attribute) ++ { ++ attribute->m_prev_attribute->m_next_attribute = 0; ++ m_last_attribute = attribute->m_prev_attribute; ++ } ++ else ++ m_first_attribute = 0; ++ attribute->m_parent = 0; ++ } ++ ++ //! Removes specified attribute from node. ++ //! \param where Pointer to attribute to be removed. ++ void remove_attribute(xml_attribute *where) ++ { ++ assert(first_attribute() && where->parent() == this); ++ if (where == m_first_attribute) ++ remove_first_attribute(); ++ else if (where == m_last_attribute) ++ remove_last_attribute(); ++ else ++ { ++ where->m_prev_attribute->m_next_attribute = where->m_next_attribute; ++ where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; ++ where->m_parent = 0; ++ } ++ } ++ ++ //! Removes all attributes of node. ++ void remove_all_attributes() ++ { ++ for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) ++ attribute->m_parent = 0; ++ m_first_attribute = 0; ++ } ++ ++ private: ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Restrictions ++ ++ // No copying ++ xml_node(const xml_node &); ++ void operator =(const xml_node &); ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Data members ++ ++ // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. ++ // This is required for maximum performance, as it allows the parser to omit initialization of ++ // unneded/redundant values. ++ // ++ // The rules are as follows: ++ // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively ++ // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage ++ // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage ++ ++ node_type m_type; // Type of node; always valid ++ xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid ++ xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero ++ xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid ++ xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero ++ xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero ++ xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero ++ ++ }; ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // XML document ++ ++ //! This class represents root of the DOM hierarchy. ++ //! It is also an xml_node and a memory_pool through public inheritance. ++ //! Use parse() function to build a DOM tree from a zero-terminated XML text string. ++ //! parse() function allocates memory for nodes and attributes by using functions of xml_document, ++ //! which are inherited from memory_pool. ++ //! To access root node of the document, use the document itself, as if it was an xml_node. ++ //! \param Ch Character type to use. ++ template ++ class xml_document: public xml_node, public memory_pool ++ { ++ ++ public: ++ ++ //! Constructs empty XML document ++ xml_document() ++ : xml_node(node_document) ++ { ++ } ++ ++ //! Parses zero-terminated XML string according to given flags. ++ //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. ++ //! The string must persist for the lifetime of the document. ++ //! In case of error, rapidxml::parse_error exception will be thrown. ++ //!

++ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. ++ //! Make sure that data is zero-terminated. ++ //!

++ //! Document can be parsed into multiple times. ++ //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. ++ //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. ++ template ++ void parse(Ch *text) ++ { ++ assert(text); ++ ++ // Remove current contents ++ this->remove_all_nodes(); ++ this->remove_all_attributes(); ++ ++ // Parse BOM, if any ++ parse_bom(text); ++ ++ // Parse children ++ while (1) ++ { ++ // Skip whitespace before node ++ skip(text); ++ if (*text == 0) ++ break; ++ ++ // Parse and append new child ++ if (*text == Ch('<')) ++ { ++ ++text; // Skip '<' ++ if (xml_node *node = parse_node(text)) ++ this->append_node(node); ++ } ++ else ++ RAPIDXML_PARSE_ERROR("expected <", text); ++ } ++ ++ } ++ ++ //! Clears the document by deleting all nodes and clearing the memory pool. ++ //! All nodes owned by document pool are destroyed. ++ void clear() ++ { ++ this->remove_all_nodes(); ++ this->remove_all_attributes(); ++ memory_pool::clear(); ++ } ++ ++ private: ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Internal character utility functions ++ ++ // Detect whitespace character ++ struct whitespace_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; ++ } ++ }; ++ ++ // Detect node name character ++ struct node_name_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; ++ } ++ }; ++ ++ // Detect attribute name character ++ struct attribute_name_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; ++ } ++ }; ++ ++ // Detect text character (PCDATA) ++ struct text_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; ++ } ++ }; ++ ++ // Detect text character (PCDATA) that does not require processing ++ struct text_pure_no_ws_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; ++ } ++ }; ++ ++ // Detect text character (PCDATA) that does not require processing ++ struct text_pure_with_ws_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; ++ } ++ }; ++ ++ // Detect attribute value character ++ template ++ struct attribute_value_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ if (Quote == Ch('\'')) ++ return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; ++ if (Quote == Ch('\"')) ++ return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; ++ return 0; // Should never be executed, to avoid warnings on Comeau ++ } ++ }; ++ ++ // Detect attribute value character ++ template ++ struct attribute_value_pure_pred ++ { ++ static unsigned char test(Ch ch) ++ { ++ if (Quote == Ch('\'')) ++ return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; ++ if (Quote == Ch('\"')) ++ return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; ++ return 0; // Should never be executed, to avoid warnings on Comeau ++ } ++ }; ++ ++ // Insert coded character, using UTF8 or 8-bit ASCII ++ template ++ static void insert_coded_character(Ch *&text, unsigned long code) ++ { ++ if (Flags & parse_no_utf8) ++ { ++ // Insert 8-bit ASCII character ++ // Todo: possibly verify that code is less than 256 and use replacement char otherwise? ++ text[0] = static_cast(code); ++ text += 1; ++ } ++ else ++ { ++ // Insert UTF8 sequence ++ if (code < 0x80) // 1 byte sequence ++ { ++ text[0] = static_cast(code); ++ text += 1; ++ } ++ else if (code < 0x800) // 2 byte sequence ++ { ++ text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; ++ text[0] = static_cast(code | 0xC0); ++ text += 2; ++ } ++ else if (code < 0x10000) // 3 byte sequence ++ { ++ text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; ++ text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; ++ text[0] = static_cast(code | 0xE0); ++ text += 3; ++ } ++ else if (code < 0x110000) // 4 byte sequence ++ { ++ text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; ++ text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; ++ text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; ++ text[0] = static_cast(code | 0xF0); ++ text += 4; ++ } ++ else // Invalid, only codes up to 0x10FFFF are allowed in Unicode ++ { ++ RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); ++ } ++ } ++ } ++ ++ // Skip characters until predicate evaluates to true ++ template ++ static void skip(Ch *&text) ++ { ++ Ch *tmp = text; ++ while (StopPred::test(*tmp)) ++ ++tmp; ++ text = tmp; ++ } ++ ++ // Skip characters until predicate evaluates to true while doing the following: ++ // - replacing XML character entity references with proper characters (' & " < > &#...;) ++ // - condensing whitespace sequences to single space character ++ template ++ static Ch *skip_and_expand_character_refs(Ch *&text) ++ { ++ // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip ++ if (Flags & parse_no_entity_translation && ++ !(Flags & parse_normalize_whitespace) && ++ !(Flags & parse_trim_whitespace)) ++ { ++ skip(text); ++ return text; ++ } ++ ++ // Use simple skip until first modification is detected ++ skip(text); ++ ++ // Use translation skip ++ Ch *src = text; ++ Ch *dest = src; ++ while (StopPred::test(*src)) ++ { ++ // If entity translation is enabled ++ if (!(Flags & parse_no_entity_translation)) ++ { ++ // Test if replacement is needed ++ if (src[0] == Ch('&')) ++ { ++ switch (src[1]) ++ { ++ ++ // & ' ++ case Ch('a'): ++ if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) ++ { ++ *dest = Ch('&'); ++ ++dest; ++ src += 5; ++ continue; ++ } ++ if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) ++ { ++ *dest = Ch('\''); ++ ++dest; ++ src += 6; ++ continue; ++ } ++ break; ++ ++ // " ++ case Ch('q'): ++ if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) ++ { ++ *dest = Ch('"'); ++ ++dest; ++ src += 6; ++ continue; ++ } ++ break; ++ ++ // > ++ case Ch('g'): ++ if (src[2] == Ch('t') && src[3] == Ch(';')) ++ { ++ *dest = Ch('>'); ++ ++dest; ++ src += 4; ++ continue; ++ } ++ break; ++ ++ // < ++ case Ch('l'): ++ if (src[2] == Ch('t') && src[3] == Ch(';')) ++ { ++ *dest = Ch('<'); ++ ++dest; ++ src += 4; ++ continue; ++ } ++ break; ++ ++ // &#...; - assumes ASCII ++ case Ch('#'): ++ if (src[2] == Ch('x')) ++ { ++ unsigned long code = 0; ++ src += 3; // Skip &#x ++ while (1) ++ { ++ unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; ++ if (digit == 0xFF) ++ break; ++ code = code * 16 + digit; ++ ++src; ++ } ++ insert_coded_character(dest, code); // Put character in output ++ } ++ else ++ { ++ unsigned long code = 0; ++ src += 2; // Skip &# ++ while (1) ++ { ++ unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; ++ if (digit == 0xFF) ++ break; ++ code = code * 10 + digit; ++ ++src; ++ } ++ insert_coded_character(dest, code); // Put character in output ++ } ++ if (*src == Ch(';')) ++ ++src; ++ else ++ RAPIDXML_PARSE_ERROR("expected ;", src); ++ continue; ++ ++ // Something else ++ default: ++ // Ignore, just copy '&' verbatim ++ break; ++ ++ } ++ } ++ } ++ ++ // If whitespace condensing is enabled ++ if (Flags & parse_normalize_whitespace) ++ { ++ // Test if condensing is needed ++ if (whitespace_pred::test(*src)) ++ { ++ *dest = Ch(' '); ++dest; // Put single space in dest ++ ++src; // Skip first whitespace char ++ // Skip remaining whitespace chars ++ while (whitespace_pred::test(*src)) ++ ++src; ++ continue; ++ } ++ } ++ ++ // No replacement, only copy character ++ *dest++ = *src++; ++ ++ } ++ ++ // Return new end ++ text = src; ++ return dest; ++ ++ } ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Internal parsing functions ++ ++ // Parse BOM, if any ++ template ++ void parse_bom(Ch *&text) ++ { ++ // UTF-8? ++ if (static_cast(text[0]) == 0xEF && ++ static_cast(text[1]) == 0xBB && ++ static_cast(text[2]) == 0xBF) ++ { ++ text += 3; // Skup utf-8 bom ++ } ++ } ++ ++ // Parse XML declaration ( ++ xml_node *parse_xml_declaration(Ch *&text) ++ { ++ // If parsing of declaration is disabled ++ if (!(Flags & parse_declaration_node)) ++ { ++ // Skip until end of declaration ++ while (text[0] != Ch('?') || text[1] != Ch('>')) ++ { ++ if (!text[0]) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ text += 2; // Skip '?>' ++ return 0; ++ } ++ ++ // Create declaration ++ xml_node *declaration = this->allocate_node(node_declaration); ++ ++ // Skip whitespace before attributes or ?> ++ skip(text); ++ ++ // Parse declaration attributes ++ parse_node_attributes(text, declaration); ++ ++ // Skip ?> ++ if (text[0] != Ch('?') || text[1] != Ch('>')) ++ RAPIDXML_PARSE_ERROR("expected ?>", text); ++ text += 2; ++ ++ return declaration; ++ } ++ ++ // Parse XML comment (' ++ return 0; // Do not produce comment node ++ } ++ ++ // Remember value start ++ Ch *value = text; ++ ++ // Skip until end of comment ++ while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) ++ { ++ if (!text[0]) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ ++ // Create comment node ++ xml_node *comment = this->allocate_node(node_comment); ++ comment->value(value, text - value); ++ ++ // Place zero terminator after comment value ++ if (!(Flags & parse_no_string_terminators)) ++ *text = Ch('\0'); ++ ++ text += 3; // Skip '-->' ++ return comment; ++ } ++ ++ // Parse DOCTYPE ++ template ++ xml_node *parse_doctype(Ch *&text) ++ { ++ // Remember value start ++ Ch *value = text; ++ ++ // Skip to > ++ while (*text != Ch('>')) ++ { ++ // Determine character type ++ switch (*text) ++ { ++ ++ // If '[' encountered, scan for matching ending ']' using naive algorithm with depth ++ // This works for all W3C test files except for 2 most wicked ++ case Ch('['): ++ { ++ ++text; // Skip '[' ++ int depth = 1; ++ while (depth > 0) ++ { ++ switch (*text) ++ { ++ case Ch('['): ++depth; break; ++ case Ch(']'): --depth; break; ++ case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ } ++ ++text; ++ } ++ break; ++ } ++ ++ // Error on end of text ++ case Ch('\0'): ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++ // Other character, skip it ++ default: ++ ++text; ++ ++ } ++ } ++ ++ // If DOCTYPE nodes enabled ++ if (Flags & parse_doctype_node) ++ { ++ // Create a new doctype node ++ xml_node *doctype = this->allocate_node(node_doctype); ++ doctype->value(value, text - value); ++ ++ // Place zero terminator after value ++ if (!(Flags & parse_no_string_terminators)) ++ *text = Ch('\0'); ++ ++ text += 1; // skip '>' ++ return doctype; ++ } ++ else ++ { ++ text += 1; // skip '>' ++ return 0; ++ } ++ ++ } ++ ++ // Parse PI ++ template ++ xml_node *parse_pi(Ch *&text) ++ { ++ // If creation of PI nodes is enabled ++ if (Flags & parse_pi_nodes) ++ { ++ // Create pi node ++ xml_node *pi = this->allocate_node(node_pi); ++ ++ // Extract PI target name ++ Ch *name = text; ++ skip(text); ++ if (text == name) ++ RAPIDXML_PARSE_ERROR("expected PI target", text); ++ pi->name(name, text - name); ++ ++ // Skip whitespace between pi target and pi ++ skip(text); ++ ++ // Remember start of pi ++ Ch *value = text; ++ ++ // Skip to '?>' ++ while (text[0] != Ch('?') || text[1] != Ch('>')) ++ { ++ if (*text == Ch('\0')) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ ++ // Set pi value (verbatim, no entity expansion or whitespace normalization) ++ pi->value(value, text - value); ++ ++ // Place zero terminator after name and value ++ if (!(Flags & parse_no_string_terminators)) ++ { ++ pi->name()[pi->name_size()] = Ch('\0'); ++ pi->value()[pi->value_size()] = Ch('\0'); ++ } ++ ++ text += 2; // Skip '?>' ++ return pi; ++ } ++ else ++ { ++ // Skip to '?>' ++ while (text[0] != Ch('?') || text[1] != Ch('>')) ++ { ++ if (*text == Ch('\0')) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ text += 2; // Skip '?>' ++ return 0; ++ } ++ } ++ ++ // Parse and append data ++ // Return character that ends data. ++ // This is necessary because this character might have been overwritten by a terminating 0 ++ template ++ Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) ++ { ++ // Backup to contents start if whitespace trimming is disabled ++ if (!(Flags & parse_trim_whitespace)) ++ text = contents_start; ++ ++ // Skip until end of data ++ Ch *value = text, *end; ++ if (Flags & parse_normalize_whitespace) ++ end = skip_and_expand_character_refs(text); ++ else ++ end = skip_and_expand_character_refs(text); ++ ++ // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > ++ if (Flags & parse_trim_whitespace) ++ { ++ if (Flags & parse_normalize_whitespace) ++ { ++ // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end ++ if (*(end - 1) == Ch(' ')) ++ --end; ++ } ++ else ++ { ++ // Backup until non-whitespace character is found ++ while (whitespace_pred::test(*(end - 1))) ++ --end; ++ } ++ } ++ ++ // If characters are still left between end and value (this test is only necessary if normalization is enabled) ++ // Create new data node ++ if (!(Flags & parse_no_data_nodes)) ++ { ++ xml_node *data = this->allocate_node(node_data); ++ data->value(value, end - value); ++ node->append_node(data); ++ } ++ ++ // Add data to parent node if no data exists yet ++ if (!(Flags & parse_no_element_values)) ++ if (*node->value() == Ch('\0')) ++ node->value(value, end - value); ++ ++ // Place zero terminator after value ++ if (!(Flags & parse_no_string_terminators)) ++ { ++ Ch ch = *text; ++ *end = Ch('\0'); ++ return ch; // Return character that ends data; this is required because zero terminator overwritten it ++ } ++ ++ // Return character that ends data ++ return *text; ++ } ++ ++ // Parse CDATA ++ template ++ xml_node *parse_cdata(Ch *&text) ++ { ++ // If CDATA is disabled ++ if (Flags & parse_no_data_nodes) ++ { ++ // Skip until end of cdata ++ while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) ++ { ++ if (!text[0]) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ text += 3; // Skip ]]> ++ return 0; // Do not produce CDATA node ++ } ++ ++ // Skip until end of cdata ++ Ch *value = text; ++ while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) ++ { ++ if (!text[0]) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ ++ // Create new cdata node ++ xml_node *cdata = this->allocate_node(node_cdata); ++ cdata->value(value, text - value); ++ ++ // Place zero terminator after value ++ if (!(Flags & parse_no_string_terminators)) ++ *text = Ch('\0'); ++ ++ text += 3; // Skip ]]> ++ return cdata; ++ } ++ ++ // Parse element node ++ template ++ xml_node *parse_element(Ch *&text) ++ { ++ // Create element node ++ xml_node *element = this->allocate_node(node_element); ++ ++ // Extract element name ++ Ch *name = text; ++ skip(text); ++ if (text == name) ++ RAPIDXML_PARSE_ERROR("expected element name", text); ++ element->name(name, text - name); ++ ++ // Skip whitespace between element name and attributes or > ++ skip(text); ++ ++ // Parse attributes, if any ++ parse_node_attributes(text, element); ++ ++ // Determine ending type ++ if (*text == Ch('>')) ++ { ++ ++text; ++ parse_node_contents(text, element); ++ } ++ else if (*text == Ch('/')) ++ { ++ ++text; ++ if (*text != Ch('>')) ++ RAPIDXML_PARSE_ERROR("expected >", text); ++ ++text; ++ } ++ else ++ RAPIDXML_PARSE_ERROR("expected >", text); ++ ++ // Place zero terminator after name ++ if (!(Flags & parse_no_string_terminators)) ++ element->name()[element->name_size()] = Ch('\0'); ++ ++ // Return parsed element ++ return element; ++ } ++ ++ // Determine node type, and parse it ++ template ++ xml_node *parse_node(Ch *&text) ++ { ++ // Parse proper node type ++ switch (text[0]) ++ { ++ ++ // <... ++ default: ++ // Parse and append element node ++ return parse_element(text); ++ ++ // (text); ++ } ++ else ++ { ++ // Parse PI ++ return parse_pi(text); ++ } ++ ++ // (text); ++ } ++ break; ++ ++ // (text); ++ } ++ break; ++ ++ // (text); ++ } ++ ++ } // switch ++ ++ // Attempt to skip other, unrecognized node types starting with ')) ++ { ++ if (*text == 0) ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++text; ++ } ++ ++text; // Skip '>' ++ return 0; // No node recognized ++ ++ } ++ } ++ ++ // Parse contents of the node - children, data etc. ++ template ++ void parse_node_contents(Ch *&text, xml_node *node) ++ { ++ // For all children and text ++ while (1) ++ { ++ // Skip whitespace between > and node contents ++ Ch *contents_start = text; // Store start of node contents before whitespace is skipped ++ skip(text); ++ Ch next_char = *text; ++ ++ // After data nodes, instead of continuing the loop, control jumps here. ++ // This is because zero termination inside parse_and_append_data() function ++ // would wreak havoc with the above code. ++ // Also, skipping whitespace after data nodes is unnecessary. ++ after_data_node: ++ ++ // Determine what comes next: node closing, child node, data node, or 0? ++ switch (next_char) ++ { ++ ++ // Node closing or child node ++ case Ch('<'): ++ if (text[1] == Ch('/')) ++ { ++ // Node closing ++ text += 2; // Skip '(text); ++ if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) ++ RAPIDXML_PARSE_ERROR("invalid closing tag name", text); ++ } ++ else ++ { ++ // No validation, just skip name ++ skip(text); ++ } ++ // Skip remaining whitespace after node name ++ skip(text); ++ if (*text != Ch('>')) ++ RAPIDXML_PARSE_ERROR("expected >", text); ++ ++text; // Skip '>' ++ return; // Node closed, finished parsing contents ++ } ++ else ++ { ++ // Child node ++ ++text; // Skip '<' ++ if (xml_node *child = parse_node(text)) ++ node->append_node(child); ++ } ++ break; ++ ++ // End of data - error ++ case Ch('\0'): ++ RAPIDXML_PARSE_ERROR("unexpected end of data", text); ++ ++ // Data node ++ default: ++ next_char = parse_and_append_data(node, text, contents_start); ++ goto after_data_node; // Bypass regular processing after data nodes ++ ++ } ++ } ++ } ++ ++ // Parse XML attributes of the node ++ template ++ void parse_node_attributes(Ch *&text, xml_node *node) ++ { ++ // For all attributes ++ while (attribute_name_pred::test(*text)) ++ { ++ // Extract attribute name ++ Ch *name = text; ++ ++text; // Skip first character of attribute name ++ skip(text); ++ if (text == name) ++ RAPIDXML_PARSE_ERROR("expected attribute name", name); ++ ++ // Create new attribute ++ xml_attribute *attribute = this->allocate_attribute(); ++ attribute->name(name, text - name); ++ node->append_attribute(attribute); ++ ++ // Skip whitespace after attribute name ++ skip(text); ++ ++ // Skip = ++ if (*text != Ch('=')) ++ RAPIDXML_PARSE_ERROR("expected =", text); ++ ++text; ++ ++ // Add terminating zero after name ++ if (!(Flags & parse_no_string_terminators)) ++ attribute->name()[attribute->name_size()] = 0; ++ ++ // Skip whitespace after = ++ skip(text); ++ ++ // Skip quote and remember if it was ' or " ++ Ch quote = *text; ++ if (quote != Ch('\'') && quote != Ch('"')) ++ RAPIDXML_PARSE_ERROR("expected ' or \"", text); ++ ++text; ++ ++ // Extract attribute value and expand char refs in it ++ Ch *value = text, *end; ++ const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes ++ if (quote == Ch('\'')) ++ end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); ++ else ++ end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); ++ ++ // Set attribute value ++ attribute->value(value, end - value); ++ ++ // Make sure that end quote is present ++ if (*text != quote) ++ RAPIDXML_PARSE_ERROR("expected ' or \"", text); ++ ++text; // Skip quote ++ ++ // Add terminating zero after value ++ if (!(Flags & parse_no_string_terminators)) ++ attribute->value()[attribute->value_size()] = 0; ++ ++ // Skip whitespace after attribute value ++ skip(text); ++ } ++ } ++ ++ }; ++ ++ //! \cond internal ++ namespace internal ++ { ++ ++ // Whitespace (space \n \r \t) ++ template ++ const unsigned char lookup_tables::lookup_whitespace[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 ++ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F ++ }; ++ ++ // Node name (anything but space \n \r \t / > ? \0) ++ template ++ const unsigned char lookup_tables::lookup_node_name[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Text (i.e. PCDATA) (anything but < \0) ++ template ++ const unsigned char lookup_tables::lookup_text[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled ++ // (anything but < \0 &) ++ template ++ const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled ++ // (anything but < \0 & space \n \r \t) ++ template ++ const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Attribute name (anything but space \n \r \t / < > = ? ! \0) ++ template ++ const unsigned char lookup_tables::lookup_attribute_name[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Attribute data with single quote (anything but ' \0) ++ template ++ const unsigned char lookup_tables::lookup_attribute_data_1[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Attribute data with single quote that does not require processing (anything but ' \0 &) ++ template ++ const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Attribute data with double quote (anything but " \0) ++ template ++ const unsigned char lookup_tables::lookup_attribute_data_2[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Attribute data with double quote that does not require processing (anything but " \0 &) ++ template ++ const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 ++ 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F ++ }; ++ ++ // Digits (dec and hex, 255 denotes end of numeric character reference) ++ template ++ const unsigned char lookup_tables::lookup_digits[256] = ++ { ++ // 0 1 2 3 4 5 6 7 8 9 A B C D E F ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 ++ 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 ++ 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E ++ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F ++ }; ++ ++ // Upper case conversion ++ template ++ const unsigned char lookup_tables::lookup_upcase[256] = ++ { ++ // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F ++ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 ++ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 ++ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 ++ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 ++ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 ++ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 ++ 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 ++ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 ++ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 ++ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 ++ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A ++ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B ++ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C ++ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D ++ 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E ++ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F ++ }; ++ } ++ //! \endcond ++ ++} ++ ++// Undefine internal macros ++#undef RAPIDXML_PARSE_ERROR ++ ++// On MSVC, restore warnings state ++#ifdef _MSC_VER ++ #pragma warning(pop) ++#endif ++ ++#endif +diff --git a/lib/rapidxml/rapidxml_iterators.hpp b/lib/rapidxml/rapidxml_iterators.hpp +new file mode 100644 +index 0000000..a827e93 +--- /dev/null ++++ b/lib/rapidxml/rapidxml_iterators.hpp +@@ -0,0 +1,175 @@ ++#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED ++#define RAPIDXML_ITERATORS_HPP_INCLUDED ++ ++// Copyright (C) 2006, 2009 Marcin Kalicinski ++// Version 1.13 ++// Revision $DateTime: 2009/05/13 01:46:17 $ ++//! \file rapidxml_iterators.hpp This file contains rapidxml iterators ++ ++#include ++#include "rapidxml.hpp" ++ ++namespace rapidxml ++{ ++ ++ //! Iterator of child nodes of xml_node ++ template ++ class node_iterator ++ { ++ ++ public: ++ ++ typedef typename xml_node value_type; ++ typedef typename xml_node &reference; ++ typedef typename xml_node *pointer; ++ typedef std::ptrdiff_t difference_type; ++ typedef std::bidirectional_iterator_tag iterator_category; ++ ++ node_iterator() ++ : m_node(0) ++ { ++ } ++ ++ node_iterator(xml_node *node) ++ : m_node(node->first_node()) ++ { ++ } ++ ++ reference operator *() const ++ { ++ assert(m_node); ++ return *m_node; ++ } ++ ++ pointer operator->() const ++ { ++ assert(m_node); ++ return m_node; ++ } ++ ++ node_iterator& operator++() ++ { ++ assert(m_node); ++ m_node = m_node->next_sibling(); ++ return *this; ++ } ++ ++ node_iterator operator++(int) ++ { ++ node_iterator tmp = *this; ++ ++this; ++ return tmp; ++ } ++ ++ node_iterator& operator--() ++ { ++ assert(m_node && m_node->previous_sibling()); ++ m_node = m_node->previous_sibling(); ++ return *this; ++ } ++ ++ node_iterator operator--(int) ++ { ++ node_iterator tmp = *this; ++ ++this; ++ return tmp; ++ } ++ ++ bool operator ==(const node_iterator &rhs) ++ { ++ return m_node == rhs.m_node; ++ } ++ ++ bool operator !=(const node_iterator &rhs) ++ { ++ return m_node != rhs.m_node; ++ } ++ ++ private: ++ ++ xml_node *m_node; ++ ++ }; ++ ++ //! Iterator of child attributes of xml_node ++ template ++ class attribute_iterator ++ { ++ ++ public: ++ ++ typedef typename xml_attribute value_type; ++ typedef typename xml_attribute &reference; ++ typedef typename xml_attribute *pointer; ++ typedef std::ptrdiff_t difference_type; ++ typedef std::bidirectional_iterator_tag iterator_category; ++ ++ attribute_iterator() ++ : m_attribute(0) ++ { ++ } ++ ++ attribute_iterator(xml_node *node) ++ : m_attribute(node->first_attribute()) ++ { ++ } ++ ++ reference operator *() const ++ { ++ assert(m_attribute); ++ return *m_attribute; ++ } ++ ++ pointer operator->() const ++ { ++ assert(m_attribute); ++ return m_attribute; ++ } ++ ++ attribute_iterator& operator++() ++ { ++ assert(m_attribute); ++ m_attribute = m_attribute->next_attribute(); ++ return *this; ++ } ++ ++ attribute_iterator operator++(int) ++ { ++ attribute_iterator tmp = *this; ++ ++this; ++ return tmp; ++ } ++ ++ attribute_iterator& operator--() ++ { ++ assert(m_attribute && m_attribute->previous_attribute()); ++ m_attribute = m_attribute->previous_attribute(); ++ return *this; ++ } ++ ++ attribute_iterator operator--(int) ++ { ++ attribute_iterator tmp = *this; ++ ++this; ++ return tmp; ++ } ++ ++ bool operator ==(const attribute_iterator &rhs) ++ { ++ return m_attribute == rhs.m_attribute; ++ } ++ ++ bool operator !=(const attribute_iterator &rhs) ++ { ++ return m_attribute != rhs.m_attribute; ++ } ++ ++ private: ++ ++ xml_attribute *m_attribute; ++ ++ }; ++ ++} ++ ++#endif +diff --git a/lib/rapidxml/rapidxml_print.hpp b/lib/rapidxml/rapidxml_print.hpp +new file mode 100644 +index 0000000..0ae2b14 +--- /dev/null ++++ b/lib/rapidxml/rapidxml_print.hpp +@@ -0,0 +1,421 @@ ++#ifndef RAPIDXML_PRINT_HPP_INCLUDED ++#define RAPIDXML_PRINT_HPP_INCLUDED ++ ++// Copyright (C) 2006, 2009 Marcin Kalicinski ++// Version 1.13 ++// Revision $DateTime: 2009/05/13 01:46:17 $ ++//! \file rapidxml_print.hpp This file contains rapidxml printer implementation ++ ++#include "rapidxml.hpp" ++ ++// Only include streams if not disabled ++#ifndef RAPIDXML_NO_STREAMS ++ #include ++ #include ++#endif ++ ++namespace rapidxml ++{ ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Printing flags ++ ++ const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. ++ ++ /////////////////////////////////////////////////////////////////////// ++ // Internal ++ ++ //! \cond internal ++ namespace internal ++ { ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Internal character operations ++ ++ // Copy characters from given range to given output iterator ++ template ++ inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) ++ { ++ while (begin != end) ++ *out++ = *begin++; ++ return out; ++ } ++ ++ // Copy characters from given range to given output iterator and expand ++ // characters into references (< > ' " &) ++ template ++ inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) ++ { ++ while (begin != end) ++ { ++ if (*begin == noexpand) ++ { ++ *out++ = *begin; // No expansion, copy character ++ } ++ else ++ { ++ switch (*begin) ++ { ++ case Ch('<'): ++ *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); ++ break; ++ case Ch('>'): ++ *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); ++ break; ++ case Ch('\''): ++ *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); ++ break; ++ case Ch('"'): ++ *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); ++ break; ++ case Ch('&'): ++ *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); ++ break; ++ default: ++ *out++ = *begin; // No expansion, copy character ++ } ++ } ++ ++begin; // Step to next character ++ } ++ return out; ++ } ++ ++ // Fill given output iterator with repetitions of the same character ++ template ++ inline OutIt fill_chars(OutIt out, int n, Ch ch) ++ { ++ for (int i = 0; i < n; ++i) ++ *out++ = ch; ++ return out; ++ } ++ ++ // Find character ++ template ++ inline bool find_char(const Ch *begin, const Ch *end) ++ { ++ while (begin != end) ++ if (*begin++ == ch) ++ return true; ++ return false; ++ } ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Internal printing operations ++ ++ // Print node ++ template ++ inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ // Print proper node type ++ switch (node->type()) ++ { ++ ++ // Document ++ case node_document: ++ out = print_children(out, node, flags, indent); ++ break; ++ ++ // Element ++ case node_element: ++ out = print_element_node(out, node, flags, indent); ++ break; ++ ++ // Data ++ case node_data: ++ out = print_data_node(out, node, flags, indent); ++ break; ++ ++ // CDATA ++ case node_cdata: ++ out = print_cdata_node(out, node, flags, indent); ++ break; ++ ++ // Declaration ++ case node_declaration: ++ out = print_declaration_node(out, node, flags, indent); ++ break; ++ ++ // Comment ++ case node_comment: ++ out = print_comment_node(out, node, flags, indent); ++ break; ++ ++ // Doctype ++ case node_doctype: ++ out = print_doctype_node(out, node, flags, indent); ++ break; ++ ++ // Pi ++ case node_pi: ++ out = print_pi_node(out, node, flags, indent); ++ break; ++ ++ // Unknown ++ default: ++ assert(0); ++ break; ++ } ++ ++ // If indenting not disabled, add line break after node ++ if (!(flags & print_no_indenting)) ++ *out = Ch('\n'), ++out; ++ ++ // Return modified iterator ++ return out; ++ } ++ ++ // Print children of the node ++ template ++ inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ for (xml_node *child = node->first_node(); child; child = child->next_sibling()) ++ out = print_node(out, child, flags, indent); ++ return out; ++ } ++ ++ // Print attributes of the node ++ template ++ inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) ++ { ++ for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) ++ { ++ if (attribute->name() && attribute->value()) ++ { ++ // Print attribute name ++ *out = Ch(' '), ++out; ++ out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); ++ *out = Ch('='), ++out; ++ // Print attribute value using appropriate quote type ++ if (find_char(attribute->value(), attribute->value() + attribute->value_size())) ++ { ++ *out = Ch('\''), ++out; ++ out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); ++ *out = Ch('\''), ++out; ++ } ++ else ++ { ++ *out = Ch('"'), ++out; ++ out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); ++ *out = Ch('"'), ++out; ++ } ++ } ++ } ++ return out; ++ } ++ ++ // Print data node ++ template ++ inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ assert(node->type() == node_data); ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); ++ return out; ++ } ++ ++ // Print data node ++ template ++ inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ assert(node->type() == node_cdata); ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ *out = Ch('<'); ++out; ++ *out = Ch('!'); ++out; ++ *out = Ch('['); ++out; ++ *out = Ch('C'); ++out; ++ *out = Ch('D'); ++out; ++ *out = Ch('A'); ++out; ++ *out = Ch('T'); ++out; ++ *out = Ch('A'); ++out; ++ *out = Ch('['); ++out; ++ out = copy_chars(node->value(), node->value() + node->value_size(), out); ++ *out = Ch(']'); ++out; ++ *out = Ch(']'); ++out; ++ *out = Ch('>'); ++out; ++ return out; ++ } ++ ++ // Print element node ++ template ++ inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ assert(node->type() == node_element); ++ ++ // Print element name and attributes, if any ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ *out = Ch('<'), ++out; ++ out = copy_chars(node->name(), node->name() + node->name_size(), out); ++ out = print_attributes(out, node, flags); ++ ++ // If node is childless ++ if (node->value_size() == 0 && !node->first_node()) ++ { ++ // Print childless node tag ending ++ *out = Ch('/'), ++out; ++ *out = Ch('>'), ++out; ++ } ++ else ++ { ++ // Print normal node tag ending ++ *out = Ch('>'), ++out; ++ ++ // Test if node contains a single data node only (and no other nodes) ++ xml_node *child = node->first_node(); ++ if (!child) ++ { ++ // If node has no children, only print its value without indenting ++ out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); ++ } ++ else if (child->next_sibling() == 0 && child->type() == node_data) ++ { ++ // If node has a sole data child, only print its value without indenting ++ out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); ++ } ++ else ++ { ++ // Print all children with full indenting ++ if (!(flags & print_no_indenting)) ++ *out = Ch('\n'), ++out; ++ out = print_children(out, node, flags, indent + 1); ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ } ++ ++ // Print node end ++ *out = Ch('<'), ++out; ++ *out = Ch('/'), ++out; ++ out = copy_chars(node->name(), node->name() + node->name_size(), out); ++ *out = Ch('>'), ++out; ++ } ++ return out; ++ } ++ ++ // Print declaration node ++ template ++ inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ // Print declaration start ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ *out = Ch('<'), ++out; ++ *out = Ch('?'), ++out; ++ *out = Ch('x'), ++out; ++ *out = Ch('m'), ++out; ++ *out = Ch('l'), ++out; ++ ++ // Print attributes ++ out = print_attributes(out, node, flags); ++ ++ // Print declaration end ++ *out = Ch('?'), ++out; ++ *out = Ch('>'), ++out; ++ ++ return out; ++ } ++ ++ // Print comment node ++ template ++ inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ assert(node->type() == node_comment); ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ *out = Ch('<'), ++out; ++ *out = Ch('!'), ++out; ++ *out = Ch('-'), ++out; ++ *out = Ch('-'), ++out; ++ out = copy_chars(node->value(), node->value() + node->value_size(), out); ++ *out = Ch('-'), ++out; ++ *out = Ch('-'), ++out; ++ *out = Ch('>'), ++out; ++ return out; ++ } ++ ++ // Print doctype node ++ template ++ inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ assert(node->type() == node_doctype); ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ *out = Ch('<'), ++out; ++ *out = Ch('!'), ++out; ++ *out = Ch('D'), ++out; ++ *out = Ch('O'), ++out; ++ *out = Ch('C'), ++out; ++ *out = Ch('T'), ++out; ++ *out = Ch('Y'), ++out; ++ *out = Ch('P'), ++out; ++ *out = Ch('E'), ++out; ++ *out = Ch(' '), ++out; ++ out = copy_chars(node->value(), node->value() + node->value_size(), out); ++ *out = Ch('>'), ++out; ++ return out; ++ } ++ ++ // Print pi node ++ template ++ inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) ++ { ++ assert(node->type() == node_pi); ++ if (!(flags & print_no_indenting)) ++ out = fill_chars(out, indent, Ch('\t')); ++ *out = Ch('<'), ++out; ++ *out = Ch('?'), ++out; ++ out = copy_chars(node->name(), node->name() + node->name_size(), out); ++ *out = Ch(' '), ++out; ++ out = copy_chars(node->value(), node->value() + node->value_size(), out); ++ *out = Ch('?'), ++out; ++ *out = Ch('>'), ++out; ++ return out; ++ } ++ ++ } ++ //! \endcond ++ ++ /////////////////////////////////////////////////////////////////////////// ++ // Printing ++ ++ //! Prints XML to given output iterator. ++ //! \param out Output iterator to print to. ++ //! \param node Node to be printed. Pass xml_document to print entire document. ++ //! \param flags Flags controlling how XML is printed. ++ //! \return Output iterator pointing to position immediately after last character of printed text. ++ template ++ inline OutIt print(OutIt out, const xml_node &node, int flags = 0) ++ { ++ return internal::print_node(out, &node, flags, 0); ++ } ++ ++#ifndef RAPIDXML_NO_STREAMS ++ ++ //! Prints XML to given output stream. ++ //! \param out Output stream to print to. ++ //! \param node Node to be printed. Pass xml_document to print entire document. ++ //! \param flags Flags controlling how XML is printed. ++ //! \return Output stream. ++ template ++ inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) ++ { ++ print(std::ostream_iterator(out), node, flags); ++ return out; ++ } ++ ++ //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. ++ //! \param out Output stream to print to. ++ //! \param node Node to be printed. ++ //! \return Output stream. ++ template ++ inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) ++ { ++ return print(out, node); ++ } ++ ++#endif ++ ++} ++ ++#endif +diff --git a/lib/rapidxml/rapidxml_utils.hpp b/lib/rapidxml/rapidxml_utils.hpp +new file mode 100644 +index 0000000..37c2953 +--- /dev/null ++++ b/lib/rapidxml/rapidxml_utils.hpp +@@ -0,0 +1,122 @@ ++#ifndef RAPIDXML_UTILS_HPP_INCLUDED ++#define RAPIDXML_UTILS_HPP_INCLUDED ++ ++// Copyright (C) 2006, 2009 Marcin Kalicinski ++// Version 1.13 ++// Revision $DateTime: 2009/05/13 01:46:17 $ ++//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful ++//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. ++ ++#include "rapidxml.hpp" ++#include ++#include ++#include ++#include ++ ++namespace rapidxml ++{ ++ ++ //! Represents data loaded from a file ++ template ++ class file ++ { ++ ++ public: ++ ++ //! Loads file into the memory. Data will be automatically destroyed by the destructor. ++ //! \param filename Filename to load. ++ file(const char *filename) ++ { ++ using namespace std; ++ ++ // Open stream ++ basic_ifstream stream(filename, ios::binary); ++ if (!stream) ++ throw runtime_error(string("cannot open file ") + filename); ++ stream.unsetf(ios::skipws); ++ ++ // Determine stream size ++ stream.seekg(0, ios::end); ++ size_t size = stream.tellg(); ++ stream.seekg(0); ++ ++ // Load data and add terminating 0 ++ m_data.resize(size + 1); ++ stream.read(&m_data.front(), static_cast(size)); ++ m_data[size] = 0; ++ } ++ ++ //! Loads file into the memory. Data will be automatically destroyed by the destructor ++ //! \param stream Stream to load from ++ file(std::basic_istream &stream) ++ { ++ using namespace std; ++ ++ // Load data and add terminating 0 ++ stream.unsetf(ios::skipws); ++ m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); ++ if (stream.fail() || stream.bad()) ++ throw runtime_error("error reading stream"); ++ m_data.push_back(0); ++ } ++ ++ //! Gets file data. ++ //! \return Pointer to data of file. ++ Ch *data() ++ { ++ return &m_data.front(); ++ } ++ ++ //! Gets file data. ++ //! \return Pointer to data of file. ++ const Ch *data() const ++ { ++ return &m_data.front(); ++ } ++ ++ //! Gets file data size. ++ //! \return Size of file data, in characters. ++ std::size_t size() const ++ { ++ return m_data.size(); ++ } ++ ++ private: ++ ++ std::vector m_data; // File data ++ ++ }; ++ ++ //! Counts children of node. Time complexity is O(n). ++ //! \return Number of children of node ++ template ++ inline std::size_t count_children(xml_node *node) ++ { ++ xml_node *child = node->first_node(); ++ std::size_t count = 0; ++ while (child) ++ { ++ ++count; ++ child = child->next_sibling(); ++ } ++ return count; ++ } ++ ++ //! Counts attributes of node. Time complexity is O(n). ++ //! \return Number of attributes of node ++ template ++ inline std::size_t count_attributes(xml_node *node) ++ { ++ xml_attribute *attr = node->first_attribute(); ++ std::size_t count = 0; ++ while (attr) ++ { ++ ++count; ++ attr = attr->next_attribute(); ++ } ++ return count; ++ } ++ ++} ++ ++#endif +diff --git a/project/BuildDependencies/scripts/zlib_d.bat b/project/BuildDependencies/scripts/zlib_d.bat +new file mode 100644 +index 0000000..669c921 +--- /dev/null ++++ b/project/BuildDependencies/scripts/zlib_d.bat +@@ -0,0 +1,13 @@ ++@ECHO OFF ++ ++SET LOC_PATH=%CD% ++SET FILES=%LOC_PATH%\zlib_d.txt ++ ++CALL dlextract.bat zlib %FILES% ++ ++cd %TMP_PATH% ++ ++xcopy include\* "%CUR_PATH%\include\" /E /Q /I /Y ++copy lib\zlib.lib "%CUR_PATH%\lib\" /Y ++ ++cd %LOC_PATH% +diff --git a/project/BuildDependencies/scripts/zlib_d.txt b/project/BuildDependencies/scripts/zlib_d.txt +new file mode 100644 +index 0000000..ac57737 +--- /dev/null ++++ b/project/BuildDependencies/scripts/zlib_d.txt +@@ -0,0 +1,2 @@ ++; filename mirror ++zlib-vc100-1.2.5-lib.tar.bz2 http://mirrors.xbmc.org/build-deps/win32/ +diff --git a/project/VS2010Express/xbmc-pvr-addons.sln b/project/VS2010Express/xbmc-pvr-addons.sln +index 3b487ed..eb3a16e 100644 +--- a/project/VS2010Express/xbmc-pvr-addons.sln ++++ b/project/VS2010Express/xbmc-pvr-addons.sln +@@ -41,6 +41,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvr.dvbviewer", "..\..\addo + EndProject + Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvr.argustv", "..\..\addons\pvr.argustv\project\VS2010Express\pvr.argustv.vcxproj", "{8D24D95E-3C4E-4DC5-ABEF-C35C938C2EBB}" + EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pvr.iptvsimple", "..\..\addons\pvr.iptvsimple\project\VS2010Express\pvr.iptvsimple.vcxproj", "{6C67B057-CE98-4732-AABF-0E374C718815}" ++EndProject + Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 +@@ -107,6 +109,10 @@ Global + {8D24D95E-3C4E-4DC5-ABEF-C35C938C2EBB}.Debug|Win32.Build.0 = Debug|Win32 + {8D24D95E-3C4E-4DC5-ABEF-C35C938C2EBB}.Release|Win32.ActiveCfg = Release|Win32 + {8D24D95E-3C4E-4DC5-ABEF-C35C938C2EBB}.Release|Win32.Build.0 = Release|Win32 ++ {6C67B057-CE98-4732-AABF-0E374C718815}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {6C67B057-CE98-4732-AABF-0E374C718815}.Debug|Win32.Build.0 = Debug|Win32 ++ {6C67B057-CE98-4732-AABF-0E374C718815}.Release|Win32.ActiveCfg = Release|Win32 ++ {6C67B057-CE98-4732-AABF-0E374C718815}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE +-- +1.8.1.6 + From ab9657878f3f70fa875c7bd368cc0d3c30a15dc2 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 11:37:51 +0200 Subject: [PATCH 10/15] scripts/image: create .tar release images, deprecate .tar.bz2 images Signed-off-by: Stephan Raue --- scripts/image | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/image b/scripts/image index 5ad4738b41..3fd8aa9cec 100755 --- a/scripts/image +++ b/scripts/image @@ -266,10 +266,12 @@ IMAGE_NAME="$DISTRONAME-$TARGET_VERSION" mkdir -p $TARGET_IMG # remove an previous created release tarball - rm -rf $TARGET_IMG/$IMAGE_NAME.tar.bz2 + rm -rf $TARGET_IMG/$IMAGE_NAME.tar + rm -rf $TARGET_IMG/$IMAGE_NAME.tar.bz2 # to remove later # create release tarball - tar cjf $TARGET_IMG/$IMAGE_NAME.tar.bz2 -C target $IMAGE_NAME + tar cf $TARGET_IMG/$IMAGE_NAME.tar -C target $IMAGE_NAME + tar cjf $TARGET_IMG/$IMAGE_NAME.tar.bz2 -C target $IMAGE_NAME # to remove later # cleanup release dir rm -rf $RELEASE_DIR From 22efa99bb0be29fe7baf741fae07e2823da019ae Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 17:14:11 +0200 Subject: [PATCH 11/15] service.openelec.settings: update to service.openelec.settings-0.1.22 Signed-off-by: Stephan Raue --- packages/mediacenter/service.openelec.settings/meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mediacenter/service.openelec.settings/meta b/packages/mediacenter/service.openelec.settings/meta index b0fac56034..90509dce99 100644 --- a/packages/mediacenter/service.openelec.settings/meta +++ b/packages/mediacenter/service.openelec.settings/meta @@ -19,7 +19,7 @@ ################################################################################ PKG_NAME="service.openelec.settings" -PKG_VERSION="0.1.21" +PKG_VERSION="0.1.22" PKG_REV="1" PKG_ARCH="any" PKG_LICENSE="prop." From 3af5874bc4bad76e77192528913331fc89e59940 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 19:56:33 +0200 Subject: [PATCH 12/15] libva-driver-intel: update to libva-driver-intel-752da5e Signed-off-by: Stephan Raue --- packages/multimedia/libva-driver-intel/meta | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/multimedia/libva-driver-intel/meta b/packages/multimedia/libva-driver-intel/meta index 30686506ed..ef3dc690fc 100644 --- a/packages/multimedia/libva-driver-intel/meta +++ b/packages/multimedia/libva-driver-intel/meta @@ -19,7 +19,7 @@ ################################################################################ PKG_NAME="libva-driver-intel" -PKG_VERSION="1.0.20" +PKG_VERSION="752da5e" PKG_REV="1" PKG_ARCH="i386 x86_64" PKG_LICENSE="GPL" From 46bebec8256dcca1dc96b1b6dc58aeffe1c58999 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Wed, 19 Jun 2013 19:56:50 +0200 Subject: [PATCH 13/15] libva: update to libva-3c15867 Signed-off-by: Stephan Raue --- packages/multimedia/libva/meta | 2 +- .../libva/patches/libva-haihao-surface-001.patch | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 packages/multimedia/libva/patches/libva-haihao-surface-001.patch diff --git a/packages/multimedia/libva/meta b/packages/multimedia/libva/meta index 46ffd533ff..ea93626098 100644 --- a/packages/multimedia/libva/meta +++ b/packages/multimedia/libva/meta @@ -19,7 +19,7 @@ ################################################################################ PKG_NAME="libva" -PKG_VERSION="1.1.1" +PKG_VERSION="3c15867" PKG_REV="1" PKG_ARCH="i386 x86_64" PKG_LICENSE="GPL" diff --git a/packages/multimedia/libva/patches/libva-haihao-surface-001.patch b/packages/multimedia/libva/patches/libva-haihao-surface-001.patch deleted file mode 100644 index c4aec12466..0000000000 --- a/packages/multimedia/libva/patches/libva-haihao-surface-001.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/va/glx/va_glx_impl.c b/va/glx/va_glx_impl.c -index 049be09..72ec9a4 100644 ---- a/va/glx/va_glx_impl.c -+++ b/va/glx/va_glx_impl.c -@@ -937,6 +937,7 @@ associate_surface( - return status; - - x11_trap_errors(); -+ status = ctx->vtable->vaSyncSurface(ctx, surface); - status = ctx->vtable->vaPutSurface( - ctx, - surface, - From 20f7603c4d55171c7c44c2579edebee0426483f8 Mon Sep 17 00:00:00 2001 From: fritsch Date: Thu, 25 Apr 2013 22:39:55 +0200 Subject: [PATCH 14/15] xbmc: Add VPP deinterlacing for intel vaapi --- ...xbmc-995.11-enable-vpp-deinterlacing.patch | 1405 +++++++++++++++++ ....12-enable-vpp-fix-audio-out-of-sync.patch | 34 + .../12.2.0/xbmc-995.13-vpp-fix-skipping.patch | 95 ++ 3 files changed, 1534 insertions(+) create mode 100644 packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.11-enable-vpp-deinterlacing.patch create mode 100644 packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.12-enable-vpp-fix-audio-out-of-sync.patch create mode 100644 packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.13-vpp-fix-skipping.patch diff --git a/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.11-enable-vpp-deinterlacing.patch b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.11-enable-vpp-deinterlacing.patch new file mode 100644 index 0000000000..f71019f76d --- /dev/null +++ b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.11-enable-vpp-deinterlacing.patch @@ -0,0 +1,1405 @@ +From 61b5f0b195a3d3d8556d5cb341beef00021acb9a Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Tue, 9 Apr 2013 23:28:36 +0200 +Subject: [PATCH 1/8] VAAPI: Add VPP class which handles bob deinterlacing + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in | 1 + + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp | 345 ++++++++++++++++++++ + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.h | 78 +++++ + 3 files changed, 424 insertions(+) + create mode 100644 xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp + create mode 100644 xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.h + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in +index c58422b..574ec3c 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/Makefile.in +@@ -9,6 +9,7 @@ SRCS += VDPAU.cpp + endif + ifeq (@USE_VAAPI@,1) + SRCS += VAAPI.cpp ++SRCS += VAAPI_VPP.cpp + endif + ifeq (@USE_CRYSTALHD@,1) + SRCS += CrystalHD.cpp +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +new file mode 100644 +index 0000000..47f98f8 +--- /dev/null ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +@@ -0,0 +1,345 @@ ++/* ++ * Copyright (C) 2005-2013 Team XBMC ++ * http://www.xbmc.org ++ * ++ * 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 "system.h" ++#ifdef HAVE_LIBVA ++ ++#include "VAAPI_VPP.h" ++#if VA_CHECK_VERSION(0,34,0) ++# define VPP_AVAIL 1 ++#endif ++ ++#ifdef VPP_AVAIL ++#include ++#endif ++ ++#include "utils/log.h" ++#include "threads/SingleLock.h" ++ ++#include ++ ++using namespace VAAPI; ++ ++bool CVPP::isSupported = true; ++ ++CVPP::CVPP() ++ :m_width(0) ++ ,m_height(0) ++ ,m_configId(VA_INVALID_ID) ++ ,m_vppReady(-1) ++ ,m_deintBobReady(-1) ++ ,m_bobContext(VA_INVALID_ID) ++ ,m_bobTargetCount(0) ++ ,m_bobFilter(VA_INVALID_ID) ++{} ++ ++CVPP::CVPP(CDisplayPtr& display, int width, int height) ++ :m_display(display) ++ ,m_width(width) ++ ,m_height(height) ++ ,m_configId(VA_INVALID_ID) ++ ,m_vppReady(0) ++ ,m_deintBobReady(0) ++ ,m_bobContext(VA_INVALID_ID) ++ ,m_bobTargetCount(0) ++ ,m_bobFilter(VA_INVALID_ID) ++{ ++ assert(display.get() && display->get()); ++ assert(width > 0 && height > 0); ++} ++ ++CVPP::~CVPP() ++{ ++ deinit(); ++} ++ ++bool CVPP::Supported() ++{ ++#ifdef VPP_AVAIL ++ return isSupported; ++#else ++ return false; ++#endif ++} ++ ++void CVPP::deinit() ++{ ++#ifdef VPP_AVAIL ++ CSingleLock lock(m_bob_lock); ++ ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - Deinitializing"); ++ ++ if(DeintBobReady() || m_deintBobReady == -100) ++ { ++ vaDestroyBuffer(m_display->get(), m_bobFilter); ++ vaDestroyContext(m_display->get(), m_bobContext); ++ delete[] m_bobTargets; ++ m_bobTargets = 0; ++ m_deintBobReady = -1; ++ ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - Deinitialized bob"); ++ } ++ ++ if(VppReady()) ++ { ++ vaDestroyConfig(m_display->get(), m_configId); ++ m_vppReady = -1; ++ ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - Deinitialized vpp"); ++ } ++#endif ++} ++ ++bool CVPP::InitVpp() ++{ ++#ifdef VPP_AVAIL ++ CSingleLock lock(m_bob_lock); ++ ++ if(VppFailed()) ++ return false; ++ ++ int numEntrypoints = vaMaxNumEntrypoints(m_display->get()); ++ VAEntrypoint *entrypoints = new VAEntrypoint[numEntrypoints]; ++ ++ if(vaQueryConfigEntrypoints(m_display->get(), VAProfileNone, entrypoints, &numEntrypoints) != VA_STATUS_SUCCESS) ++ { ++ delete[] entrypoints; ++ ++ CLog::Log(LOGERROR, "VAAPI_VPP - failed querying entrypoints"); ++ ++ m_vppReady = -1; ++ return false; ++ } ++ ++ int i; ++ for(i = 0; i < numEntrypoints; ++i) ++ if(entrypoints[i] == VAEntrypointVideoProc) ++ break; ++ delete[] entrypoints; ++ ++ if(i >= numEntrypoints) ++ { ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - Entrypoint VideoProc not supported"); ++ isSupported = false; ++ m_vppReady = -1; ++ return false; ++ } ++ ++ if(vaCreateConfig(m_display->get(), VAProfileNone, VAEntrypointVideoProc, NULL, 0, &m_configId) != VA_STATUS_SUCCESS) ++ { ++ CLog::Log(LOGERROR, "VAAPI_VPP - failed creating va config"); ++ m_vppReady = -1; ++ return false; ++ } ++ ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - Successfully initialized VPP"); ++ ++ m_vppReady = 1; ++ return true; ++#else ++ m_vppReady = -1; ++ return false; ++#endif ++} ++ ++bool CVPP::InitDeintBob(int num_surfaces) ++{ ++#ifdef VPP_AVAIL ++ CSingleLock lock(m_bob_lock); ++ ++ assert(VppReady()); ++ ++ if(DeintBobFailed()) ++ return false; ++ ++ VASurfaceID *bobTargetsInternal = new VASurfaceID[num_surfaces]; ++ m_bobTargetCount = num_surfaces; ++ if(vaCreateSurfaces(m_display->get(), VA_RT_FORMAT_YUV420, m_width, m_height, bobTargetsInternal, m_bobTargetCount, NULL, 0) != VA_STATUS_SUCCESS) ++ { ++ CLog::Log(LOGERROR, "VAAPI_VPP - failed creating bob target surface"); ++ m_deintBobReady = -1; ++ return false; ++ } ++ ++ if(vaCreateContext(m_display->get(), m_configId, m_width, m_height, VA_PROGRESSIVE, bobTargetsInternal, m_bobTargetCount, &m_bobContext) != VA_STATUS_SUCCESS) ++ { ++ CLog::Log(LOGERROR, "VAAPI_VPP - failed creating bob deint context"); ++ vaDestroySurfaces(m_display->get(), bobTargetsInternal, m_bobTargetCount); ++ m_deintBobReady = -1; ++ return false; ++ } ++ ++ VAProcFilterType filters[VAProcFilterCount]; ++ unsigned int numFilters = VAProcFilterCount; ++ VAProcFilterCapDeinterlacing deinterlacingCaps[VAProcDeinterlacingCount]; ++ unsigned int numDeinterlacingCaps = VAProcDeinterlacingCount; ++ ++ if( ++ vaQueryVideoProcFilters(m_display->get(), m_bobContext, filters, &numFilters) != VA_STATUS_SUCCESS ++ || vaQueryVideoProcFilterCaps(m_display->get(), m_bobContext, VAProcFilterDeinterlacing, deinterlacingCaps, &numDeinterlacingCaps) != VA_STATUS_SUCCESS) ++ { ++ vaDestroyContext(m_display->get(), m_bobContext); ++ vaDestroySurfaces(m_display->get(), bobTargetsInternal, m_bobTargetCount); ++ CLog::Log(LOGERROR, "VAAPI_VPP - failed querying filter caps"); ++ m_deintBobReady = -1; ++ return false; ++ } ++ ++ bool bobSupported = false; ++ for(unsigned int fi = 0; fi < numFilters; ++fi) ++ if(filters[fi] == VAProcFilterDeinterlacing) ++ for(unsigned int ci = 0; ci < numDeinterlacingCaps; ++ci) ++ { ++ if(deinterlacingCaps[ci].type != VAProcDeinterlacingBob) ++ continue; ++ bobSupported = true; ++ break; ++ } ++ ++ if(!bobSupported) ++ { ++ vaDestroyContext(m_display->get(), m_bobContext); ++ vaDestroySurfaces(m_display->get(), bobTargetsInternal, m_bobTargetCount); ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - bob deinterlacing filter is not supported"); ++ isSupported = false; ++ m_deintBobReady = -1; ++ return false; ++ } ++ ++ VAProcFilterParameterBufferDeinterlacing deint; ++ deint.type = VAProcFilterDeinterlacing; ++ deint.algorithm = VAProcDeinterlacingBob; ++ deint.flags = 0; ++ ++ if(vaCreateBuffer(m_display->get(), m_bobContext, VAProcFilterParameterBufferType, sizeof(deint), 1, &deint, &m_bobFilter) != VA_STATUS_SUCCESS) ++ { ++ vaDestroyContext(m_display->get(), m_bobContext); ++ vaDestroySurfaces(m_display->get(), bobTargetsInternal, m_bobTargetCount); ++ CLog::Log(LOGERROR, "VAAPI_VPP - creating ProcFilterParameterBuffer failed"); ++ m_deintBobReady = -1; ++ return false; ++ } ++ ++ CLog::Log(LOGDEBUG, "VAAPI_VPP - Successfully initialized bob"); ++ ++ m_bobTargets = new CSurfacePtr[m_bobTargetCount]; ++ for(int i = 0; i < m_bobTargetCount; ++i) ++ m_bobTargets[i] = CSurfacePtr(new CSurface(bobTargetsInternal[i], m_display)); ++ ++ m_deintBobReady = 1; ++ return true; ++#else ++ m_deintBobReady = -1; ++ return false; ++#endif ++} ++ ++#define CHECK_VA(s, b) \ ++{\ ++ VAStatus res = s;\ ++ if(res != VA_STATUS_SUCCESS)\ ++ {\ ++ CLog::Log(LOGERROR, "VAAPI_VPP - failed executing "#s" at line %d with error %x:%s", __LINE__, res, vaErrorStr(res));\ ++ VABufferID buf = b;\ ++ if(buf != 0)\ ++ vaDestroyBuffer(m_display->get(), buf);\ ++ m_deintBobReady = -100;\ ++ return CSurfacePtr();\ ++ }\ ++} ++ ++CSurfacePtr CVPP::DeintBob(const CSurfacePtr& input, bool topField) ++{ ++#ifdef VPP_AVAIL ++ CSingleLock lock(m_bob_lock); ++ ++ if(!DeintBobReady()) ++ return CSurfacePtr(); ++ ++ CSurfacePtr bobTarget = getFreeSurface(); ++ if(!bobTarget.get()) ++ return CSurfacePtr(); ++ ++ VABufferID pipelineBuf; ++ VAProcPipelineParameterBuffer *pipelineParam; ++ VARectangle inputRegion; ++ VARectangle outputRegion; ++ ++ CHECK_VA(vaBeginPicture(m_display->get(), m_bobContext, bobTarget->m_id), 0); ++ ++ CHECK_VA(vaCreateBuffer(m_display->get(), m_bobContext, VAProcPipelineParameterBufferType, sizeof(VAProcPipelineParameterBuffer), 1, NULL, &pipelineBuf), 0); ++ CHECK_VA(vaMapBuffer(m_display->get(), pipelineBuf, (void**)&pipelineParam), pipelineBuf); ++ ++ // This input/output regions crop two the top and bottom pixel rows ++ // It removes the flickering pixels caused by bob ++ const int vcrop = 1; ++ const int hcrop = 0; ++ inputRegion.x = outputRegion.x = hcrop; ++ inputRegion.y = outputRegion.y = vcrop; ++ inputRegion.width = outputRegion.width = m_width - (2 * hcrop); ++ inputRegion.height = outputRegion.height = m_height - (2 * vcrop); ++ ++ pipelineParam->surface = input->m_id; ++ pipelineParam->output_region = &outputRegion; ++ pipelineParam->surface_region = &inputRegion; ++ pipelineParam->output_background_color = 0xff000000; ++ ++ pipelineParam->filter_flags = topField ? VA_TOP_FIELD : VA_BOTTOM_FIELD; ++ pipelineParam->filters = &m_bobFilter; ++ pipelineParam->num_filters = 1; ++ ++ pipelineParam->num_forward_references = 0; ++ pipelineParam->forward_references = NULL; ++ pipelineParam->num_backward_references = 0; ++ pipelineParam->backward_references = NULL; ++ ++ CHECK_VA(vaUnmapBuffer(m_display->get(), pipelineBuf), pipelineBuf); ++ ++ CHECK_VA(vaRenderPicture(m_display->get(), m_bobContext, &pipelineBuf, 1), pipelineBuf); ++ CHECK_VA(vaEndPicture(m_display->get(), m_bobContext), pipelineBuf); ++ ++ CHECK_VA(vaDestroyBuffer(m_display->get(), pipelineBuf), 0); ++ ++ CHECK_VA(vaSyncSurface(m_display->get(), bobTarget->m_id), 0); ++ ++ return bobTarget; ++#else ++ return VA_INVALID_ID; ++#endif ++} ++ ++CSurfacePtr CVPP::getFreeSurface() ++{ ++#ifdef VPP_AVAIL ++ for(int i = 0; i < m_bobTargetCount; ++i) ++ { ++ if(m_bobTargets[i].use_count() == 1) ++ { ++ return m_bobTargets[i]; ++ } ++ } ++ ++ CLog::Log(LOGWARNING, "VAAPI_VPP - Running out of surfaces"); ++#endif ++ ++ return CSurfacePtr(); ++} ++ ++#endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.h +new file mode 100644 +index 0000000..5fa3ed6 +--- /dev/null ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.h +@@ -0,0 +1,78 @@ ++/* ++ * Copyright (C) 2005-2013 Team XBMC ++ * http://www.xbmc.org ++ * ++ * 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 ++ * ++ */ ++#pragma once ++ ++#include "VAAPI.h" ++#include "threads/CriticalSection.h" ++ ++#include ++#include ++ ++namespace VAAPI ++{ ++ class CVPP ++ { ++ friend class VPPSurfaceDeleter; ++ CVPP(); ++ ++ public: ++ CVPP(CDisplayPtr& display, int width, int height); ++ ~CVPP(); ++ ++ static bool Supported(); ++ ++ void deinit(); ++ ++ bool InitVpp(); ++ bool InitDeintBob(int num_surfaces); ++ ++ inline bool VppReady() { return m_vppReady > 0; } ++ inline bool VppFailed() { return m_vppReady < 0; } ++ inline bool DeintBobReady() { return m_deintBobReady > 0; } ++ inline bool DeintBobFailed() { return m_deintBobReady < 0; } ++ ++ CSurfacePtr DeintBob(const CSurfacePtr& input, bool topField); ++ ++ private: ++ CSurfacePtr getFreeSurface(); ++ ++ ++ static bool isSupported; ++ ++ ++ CDisplayPtr m_display; ++ int m_width; ++ int m_height; ++ ++ VAConfigID m_configId; ++ ++ int m_vppReady; ++ int m_deintBobReady; ++ ++ //VPP Bob Deinterlacing ++ CCriticalSection m_bob_lock; ++ VAContextID m_bobContext; ++ int m_bobTargetCount; ++ CSurfacePtr *m_bobTargets; ++ VABufferID m_bobFilter; ++ }; ++ ++} ++ +-- +1.7.10.4 + + +From 701ac98da10ebd8255943ccb9031e431b383d09b Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Tue, 9 Apr 2013 23:29:19 +0200 +Subject: [PATCH 2/8] VAAPI: Integrate VPP into VAAPI implementation (resolve + conflicts) + +--- + language/English/strings.po | 6 +- + xbmc/cores/VideoRenderers/LinuxRendererGL.cpp | 14 ++ + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp | 319 +++++++++++++++++++++--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h | 66 +++++ + xbmc/settings/VideoSettings.h | 2 + + xbmc/video/dialogs/GUIDialogVideoSettings.cpp | 1 + + 6 files changed, 374 insertions(+), 34 deletions(-) + +diff --git a/language/English/strings.po b/language/English/strings.po +index 17bc493..b5a07f7 100644 +--- a/language/English/strings.po ++++ b/language/English/strings.po +@@ -6354,7 +6354,11 @@ msgctxt "#16326" + msgid "XVBA" + msgstr "" + +-#empty strings from id 16327 to 16399 ++msgctxt "#16327" ++msgid "VAAPI Auto" ++msgstr "" ++ ++#empty strings from id 16328 to 16399 + + msgctxt "#16400" + msgid "Post-processing" +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +index 147dc06..3f54607 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGL.cpp +@@ -54,6 +54,7 @@ + #include + #include + #include "cores/dvdplayer/DVDCodecs/Video/VAAPI.h" ++#include "cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.h" + + #define USE_VAAPI_GLX_BIND \ + (VA_MAJOR_VERSION == 0 && \ +@@ -3659,6 +3660,11 @@ bool CLinuxRendererGL::Supports(EINTERLACEMETHOD method) + if(m_renderMethod & RENDER_VAAPI) + { + #ifdef HAVE_LIBVA ++ if(method == VS_INTERLACEMETHOD_VAAPI_AUTO) ++ { ++ return VAAPI::CVPP::Supported(); ++ } ++ + VAAPI::CDisplayPtr disp = m_buffers[m_iYV12RenderBuffer].vaapi.display; + if(disp) + { +@@ -3756,6 +3762,14 @@ EINTERLACEMETHOD CLinuxRendererGL::AutoInterlaceMethod() + if(m_renderMethod & RENDER_VDPAU) + return VS_INTERLACEMETHOD_NONE; + ++ if(m_renderMethod & RENDER_VAAPI) ++ { ++#ifdef HAVE_LIBVA ++ if(VAAPI::CVPP::Supported()) ++ return VS_INTERLACEMETHOD_VAAPI_AUTO; ++#endif ++ } ++ + if(Supports(VS_INTERLACEMETHOD_RENDER_BOB)) + return VS_INTERLACEMETHOD_RENDER_BOB; + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index 43a05b3..2079f0a 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -20,8 +20,11 @@ + #include "system.h" + #ifdef HAVE_LIBVA + #include "windowing/WindowingFactory.h" ++#include "settings/MediaSettings.h" + #include "settings/Settings.h" ++#include "cores/dvdplayer/DVDClock.h" + #include "VAAPI.h" ++#include "VAAPI_VPP.h" + #include "DVDVideoCodec.h" + #include + #include +@@ -147,6 +150,7 @@ CDecoder::CDecoder() + m_surfaces_count = 0; + m_config = 0; + m_context = 0; ++ m_vppth = 0; + m_hwaccel = (vaapi_context*)calloc(1, sizeof(vaapi_context)); + memset(m_surfaces, 0, sizeof(*m_surfaces)); + } +@@ -203,13 +207,13 @@ int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic) + else + { + // To avoid stutter, we scan the free surface pool (provided by decoder) for surfaces +- // that are 100% not in use by renderer. The pointers to these surfaces have a use_count of 1. +- for (; it != m_surfaces_free.end() && it->use_count() > 1; it++) {} ++ // that are 100% not in use by renderer or vpp. The pointers to these surfaces have a use_count of 1. ++ for (; it != m_surfaces_free.end() && it->use_count() > 1; ++it) {} + + // If we have zero free surface from decoder OR all free surfaces are in use by renderer, we allocate a new surface + if (it == m_surfaces_free.end()) + { +- if (!m_surfaces_free.empty()) CLog::Log(LOGERROR, "VAAPI - renderer still using all freed up surfaces by decoder"); ++ if (!m_surfaces_free.empty()) CLog::Log(LOGERROR, "VAAPI - renderer/vpp still using all freed up surfaces by decoder"); + CLog::Log(LOGERROR, "VAAPI - unable to find free surface, trying to allocate a new one"); + if(!EnsureSurfaces(avctx, m_surfaces_count+1) || m_surfaces_free.empty()) + { +@@ -230,7 +234,7 @@ int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic) + pic->data[0] = (uint8_t*)wrapper; + pic->data[1] = NULL; + pic->data[2] = NULL; +- pic->data[3] = (uint8_t*)surface; ++ pic->data[3] = (uint8_t*)(uintptr_t)surface; + pic->linesize[0] = 0; + pic->linesize[1] = 0; + pic->linesize[2] = 0; +@@ -240,7 +244,11 @@ int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic) + } + + void CDecoder::Close() +-{ ++{ ++ if(m_vppth) ++ delete m_vppth; ++ m_vppth = 0; ++ + if(m_context) + WARN(vaDestroyContext(m_display->get(), m_context)) + m_context = 0; +@@ -370,6 +378,10 @@ bool CDecoder::Open(AVCodecContext *avctx, enum PixelFormat fmt, unsigned int su + if (!EnsureContext(avctx)) + return false; + ++ m_vppth = new CVPPThread(m_display, avctx->width, avctx->height); ++ m_vppth->Init(m_refs); // Ignore result, VPPThread just passes frames if init failed ++ m_vppth->Start(); ++ + m_hwaccel->display = m_display->get(); + + avctx->hwaccel_context = m_hwaccel; +@@ -398,7 +410,12 @@ bool CDecoder::EnsureContext(AVCodecContext *avctx) + else + m_refs = 2; + } +- return EnsureSurfaces(avctx, m_refs + m_renderbuffers_count + 1); ++ ++ int vpp_buf = 0; ++ if(CVPP::Supported()) ++ vpp_buf = 4; ++ ++ return EnsureSurfaces(avctx, m_refs + m_renderbuffers_count + vpp_buf + 3); + } + + bool CDecoder::EnsureSurfaces(AVCodecContext *avctx, unsigned n_surfaces_count) +@@ -448,45 +465,69 @@ int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame) + return status; + + if(frame) +- return VC_BUFFER | VC_PICTURE; +- else +- return VC_BUFFER; +-} +- +-bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) +-{ +- ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(picture); +- VASurfaceID surface = GetSurfaceID(frame); +- ++ { ++ CVPPDecodedPicture picture; ++ picture.valid = true; + +- m_holder.surface.reset(); ++ memset(&picture.DVDPic, 0, sizeof(picture.DVDPic)); ++ ((CDVDVideoCodecFFmpeg*)avctx->opaque)->GetPictureCommon(&picture.DVDPic); ++ VASurfaceID surface = GetSurfaceID(frame); + +- std::list::iterator it; +- for(it = m_surfaces_used.begin(); it != m_surfaces_used.end() && !m_holder.surface; it++) +- { +- if((*it)->m_id == surface) +- { +- m_holder.surface = *it; +- break; ++ std::list::iterator it; ++ for(it = m_surfaces_used.begin(); it != m_surfaces_used.end() && !m_holder.surface; ++it) ++ { ++ if((*it)->m_id == surface) ++ { ++ picture.surface = *it; ++ break; ++ } + } +- } +- +- for(it = m_surfaces_free.begin(); it != m_surfaces_free.end() && !m_holder.surface; it++) +- { +- if((*it)->m_id == surface) ++ for(it = m_surfaces_free.begin(); it != m_surfaces_free.end() && !m_holder.surface; ++it) ++ { ++ if((*it)->m_id == surface) ++ { ++ picture.surface = *it; ++ break; ++ } ++ } ++ if(!picture.surface) + { +- m_holder.surface = *it; +- break; ++ CLog::Log(LOGERROR, "VAAPI - Unable to find surface"); ++ return VC_ERROR; + } ++ ++ m_vppth->InsertNewFrame(picture); + } +- if(!m_holder.surface) ++ ++ m_vppth->WaitForOutput(2000); ++ ++ int ret = 0; ++ ++ if(m_vppth->GetInputQueueSize() < 4 && m_vppth->GetOutputQueueSize() < 8) ++ ret |= VC_BUFFER; ++ if(m_vppth->GetOutputQueueSize() > 0) ++ ret |= VC_PICTURE; ++ ++ return ret; ++} ++ ++bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture) ++{ ++ m_holder.surface.reset(); ++ ++ CVPPRenderPicture outPic = m_vppth->GetOutputPicture(); ++ if(!outPic.valid) + { +- CLog::Log(LOGERROR, "VAAPI - Unable to find surface"); ++ CLog::Log(LOGERROR, "VAAPI - Got an invalid render picture"); + return false; + } + ++ m_holder.surface = outPic.surface; ++ *picture = outPic.DVDPic; ++ + picture->format = RENDER_FMT_VAAPI; + picture->vaapi = &m_holder; ++ + return true; + } + +@@ -521,4 +562,216 @@ int CDecoder::Check(AVCodecContext* avctx) + return 0; + } + ++ ++CVPPThread::CVPPThread(CDisplayPtr& display, int width, int height) ++ :CThread("VAAPI VPP Thread") ++ ,m_stop(false) ++{ ++ m_vpp = new CVPP(display, width, height); ++} ++ ++CVPPThread::~CVPPThread() ++{ ++ Dispose(); ++} ++ ++bool CVPPThread::Init(int num_refs) ++{ ++ if(!m_vpp->InitVpp()) ++ return false; ++ ++ if(!m_vpp->InitDeintBob(num_refs + 16)) ++ return false; ++ ++ return true; ++} ++ ++void CVPPThread::Start() ++{ ++ m_stop = false; ++ Create(); ++} ++ ++void CVPPThread::Dispose() ++{ ++ m_stop = true; ++ m_input_cond.notifyAll(); ++ StopThread(); ++ m_output_cond.notifyAll(); ++ ++ m_input_queue = std::queue(); ++ m_output_queue = std::queue(); ++ ++ if(m_vpp) ++ { ++ delete m_vpp; ++ m_vpp = 0; ++ } ++} ++ ++void CVPPThread::OnStartup() ++{ ++ CLog::Log(LOGDEBUG, "VAAPI - VPP thread on startup"); ++} ++ ++void CVPPThread::OnExit() ++{ ++ CLog::Log(LOGDEBUG, "VAAPI - VPP thread on exit"); ++} ++ ++void CVPPThread::InsertNewFrame(CVPPDecodedPicture &new_frame) ++{ ++ if(!IsRunning()) ++ return; ++ ++ m_input_queue_lock.lock(); ++ ++ m_input_queue.push(new_frame); ++ ++ m_input_cond.notify(); ++ m_input_queue_lock.unlock(); ++} ++ ++void CVPPThread::WaitForOutput(unsigned long msec) ++{ ++ if(!IsRunning()) ++ return; ++ ++ m_output_queue_lock.lock(); ++ if(m_output_queue.empty()) ++ { ++ if(msec > 0) ++ m_output_cond.wait(m_output_queue_lock, msec); ++ else ++ m_output_cond.wait(m_output_queue_lock); ++ } ++ m_output_queue_lock.unlock(); ++} ++ ++CVPPRenderPicture CVPPThread::GetOutputPicture() ++{ ++ CVPPRenderPicture res = CVPPRenderPicture(); ++ ++ if(!IsRunning()) ++ return res; ++ ++ m_output_queue_lock.lock(); ++ ++ if(!m_output_queue.empty()) ++ { ++ res = m_output_queue.front(); ++ m_output_queue.pop(); ++ } ++ ++ m_output_queue_lock.unlock(); ++ ++ return res; ++} ++ ++CVPPDecodedPicture CVPPThread::GetCurrentFrame() ++{ ++ CVPPDecodedPicture res = CVPPDecodedPicture(); ++ ++ if(m_stop) ++ return res; ++ ++ m_input_queue_lock.lock(); ++ ++ if(m_input_queue.empty()) ++ m_input_cond.wait(m_input_queue_lock); ++ ++ if(!m_input_queue.empty()) ++ { ++ res = m_input_queue.front(); ++ m_input_queue.pop(); ++ } ++ ++ m_input_queue_lock.unlock(); ++ ++ return res; ++} ++ ++void CVPPThread::InsertOutputFrame(CVPPRenderPicture &new_frame) ++{ ++ m_output_queue_lock.lock(); ++ ++ m_output_queue.push(new_frame); ++ ++ m_output_cond.notify(); ++ m_output_queue_lock.unlock(); ++} ++ ++int CVPPThread::GetInputQueueSize() ++{ ++ CSingleLock lock(m_input_queue_lock); ++ return m_input_queue.size(); ++} ++ ++int CVPPThread::GetOutputQueueSize() ++{ ++ CSingleLock lock(m_output_queue_lock); ++ return m_output_queue.size(); ++} ++ ++void CVPPThread::DoDeinterlacing(const CVPPDecodedPicture &frame, bool topField) ++{ ++ if(!m_vpp->DeintBobReady()) ++ return; ++ ++ CSurfacePtr surf = m_vpp->DeintBob(frame.surface, topField); ++ if(!surf.get()) ++ return; ++ ++ CVPPRenderPicture res; ++ res.valid = true; ++ res.surface = surf; ++ res.DVDPic = frame.DVDPic; ++ ++ res.DVDPic.iFlags &= ~(DVP_FLAG_TOP_FIELD_FIRST | DVP_FLAG_REPEAT_TOP_FIELD | DVP_FLAG_INTERLACED); ++ ++ if( ((frame.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST) && !topField) ++ || (!(frame.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST) && topField) ) ++ { ++ res.DVDPic.pts = DVD_NOPTS_VALUE; ++ res.DVDPic.dts = DVD_NOPTS_VALUE; ++ } ++ res.DVDPic.iRepeatPicture = 0.0; ++ ++ InsertOutputFrame(res); ++} ++ ++void CVPPThread::Process() ++{ ++ CVPPDecodedPicture currentFrame = CVPPDecodedPicture(); ++ ++ while(!m_stop) ++ { ++ if(currentFrame.valid) ++ { ++ bool isInterlaced = currentFrame.DVDPic.iFlags & DVP_FLAG_INTERLACED; ++ EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode; ++ EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod; ++ ++ if (m_vpp->DeintBobReady() && (method == VS_INTERLACEMETHOD_VAAPI_AUTO || method == VS_INTERLACEMETHOD_AUTO) ++ && (mode == VS_DEINTERLACEMODE_FORCE || (mode == VS_DEINTERLACEMODE_AUTO && isInterlaced))) ++ { ++ bool topField = currentFrame.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST; ++ DoDeinterlacing(currentFrame, topField); ++ DoDeinterlacing(currentFrame, !topField); ++ } ++ else ++ { ++ CVPPRenderPicture res; ++ res.valid = true; ++ res.DVDPic = currentFrame.DVDPic; ++ res.surface = currentFrame.surface; ++ InsertOutputFrame(res); ++ } ++ } ++ ++ currentFrame = CVPPDecodedPicture(); ++ currentFrame = GetCurrentFrame(); ++ } ++} ++ + #endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +index 417cbc0..241ff38 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +@@ -23,11 +23,16 @@ + + #include "DllAvCodec.h" + #include "DVDVideoCodecFFmpeg.h" ++#include "threads/Thread.h" ++#include "threads/Condition.h" ++#include "threads/CriticalSection.h" ++ + #include + #include + #include + #include + #include ++#include + #include + + +@@ -97,6 +102,65 @@ struct CHolder + {} + }; + ++class CVPP; ++ ++struct CVPPDecodedPicture ++{ ++ CVPPDecodedPicture():valid(false) {} ++ ++ bool valid; ++ DVDVideoPicture DVDPic; ++ CSurfacePtr surface; ++}; ++ ++struct CVPPRenderPicture ++{ ++ CVPPRenderPicture():valid(false) {} ++ ++ bool valid; ++ DVDVideoPicture DVDPic; ++ CSurfacePtr surface; ++}; ++ ++class CVPPThread : private CThread ++{ ++public: ++ CVPPThread(CDisplayPtr& display, int width, int height); ++ ~CVPPThread(); ++ ++ bool Init(int num_refs); ++ void Start(); ++ void Dispose(); ++ ++ void InsertNewFrame(CVPPDecodedPicture &new_frame); ++ void WaitForOutput(unsigned long msec = 0); ++ CVPPRenderPicture GetOutputPicture(); ++ ++ int GetInputQueueSize(); ++ int GetOutputQueueSize(); ++ ++protected: ++ void OnStartup(); ++ void OnExit(); ++ void Process(); ++ ++ void InsertOutputFrame(CVPPRenderPicture &new_frame); ++ CVPPDecodedPicture GetCurrentFrame(); ++ void DoDeinterlacing(const CVPPDecodedPicture &frame, bool topField); ++ ++ CVPP *m_vpp; ++ ++ bool m_stop; ++ ++ CCriticalSection m_input_queue_lock; ++ XbmcThreads::ConditionVariable m_input_cond; ++ std::queue m_input_queue; ++ ++ CCriticalSection m_output_queue_lock; ++ XbmcThreads::ConditionVariable m_output_cond; ++ std::queue m_output_queue; ++}; ++ + class CDecoder + : public CDVDVideoCodecFFmpeg::IHardwareDecoder + { +@@ -134,6 +198,8 @@ protected: + + vaapi_context *m_hwaccel; + ++ CVPPThread *m_vppth; ++ + CHolder m_holder; // silly struct to pass data to renderer + }; + +diff --git a/xbmc/settings/VideoSettings.h b/xbmc/settings/VideoSettings.h +index f54a837..5e37d0c 100644 +--- a/xbmc/settings/VideoSettings.h ++++ b/xbmc/settings/VideoSettings.h +@@ -67,6 +67,8 @@ enum EINTERLACEMETHOD + + VS_INTERLACEMETHOD_XVBA = 22, + ++ VS_INTERLACEMETHOD_VAAPI_AUTO = 23, ++ + VS_INTERLACEMETHOD_MAX // do not use and keep as last enum value. + }; + +diff --git a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp +index f6b1ea4..08702dd 100644 +--- a/xbmc/video/dialogs/GUIDialogVideoSettings.cpp ++++ b/xbmc/video/dialogs/GUIDialogVideoSettings.cpp +@@ -111,6 +111,7 @@ void CGUIDialogVideoSettings::CreateSettings() + entries.push_back(make_pair(VS_INTERLACEMETHOD_DXVA_BEST , 16321)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_AUTO_ION , 16325)); + entries.push_back(make_pair(VS_INTERLACEMETHOD_XVBA , 16326)); ++ entries.push_back(make_pair(VS_INTERLACEMETHOD_VAAPI_AUTO , 16327)); + + /* remove unsupported methods */ + for(vector >::iterator it = entries.begin(); it != entries.end();) +-- +1.7.10.4 + + +From 201720df44515b512c2e1bd77a869c1537c05d44 Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Wed, 10 Apr 2013 10:38:01 +0200 +Subject: [PATCH 3/8] VAAPI: Add Flush function + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp | 29 ++++++++++++++++++++++++ + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h | 6 +++++ + 2 files changed, 35 insertions(+) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index 2079f0a..aedeb0f 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -531,6 +531,11 @@ bool CDecoder::GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture + return true; + } + ++void CDecoder::Reset() ++{ ++ m_vppth->Flush(); ++} ++ + int CDecoder::Check(AVCodecContext* avctx) + { + if (m_display == NULL) +@@ -713,6 +718,19 @@ int CVPPThread::GetOutputQueueSize() + return m_output_queue.size(); + } + ++void CVPPThread::Flush() ++{ ++ CSingleLock lock(m_work_lock); ++ ++ m_input_queue_lock.lock(); ++ m_input_queue = std::queue(); ++ m_input_queue_lock.unlock(); ++ ++ m_output_queue_lock.lock(); ++ m_output_queue = std::queue(); ++ m_output_queue_lock.unlock(); ++} ++ + void CVPPThread::DoDeinterlacing(const CVPPDecodedPicture &frame, bool topField) + { + if(!m_vpp->DeintBobReady()) +@@ -744,11 +762,16 @@ void CVPPThread::Process() + { + CVPPDecodedPicture currentFrame = CVPPDecodedPicture(); + ++ m_work_lock.lock(); ++ + while(!m_stop) + { + if(currentFrame.valid) + { + bool isInterlaced = currentFrame.DVDPic.iFlags & DVP_FLAG_INTERLACED; ++ //if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) ++ // isInterlaced = false; ++ + EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode; + EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod; + +@@ -756,6 +779,7 @@ void CVPPThread::Process() + && (mode == VS_DEINTERLACEMODE_FORCE || (mode == VS_DEINTERLACEMODE_AUTO && isInterlaced))) + { + bool topField = currentFrame.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST; ++ + DoDeinterlacing(currentFrame, topField); + DoDeinterlacing(currentFrame, !topField); + } +@@ -770,8 +794,13 @@ void CVPPThread::Process() + } + + currentFrame = CVPPDecodedPicture(); ++ ++ m_work_lock.unlock(); + currentFrame = GetCurrentFrame(); ++ m_work_lock.lock(); + } ++ ++ m_work_lock.unlock(); + } + + #endif +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +index 241ff38..1aa2fd7 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +@@ -139,6 +139,8 @@ public: + int GetInputQueueSize(); + int GetOutputQueueSize(); + ++ void Flush(); ++ + protected: + void OnStartup(); + void OnExit(); +@@ -152,6 +154,8 @@ protected: + + bool m_stop; + ++ CCriticalSection m_work_lock; ++ + CCriticalSection m_input_queue_lock; + XbmcThreads::ConditionVariable m_input_cond; + std::queue m_input_queue; +@@ -173,9 +177,11 @@ public: + virtual int Decode (AVCodecContext* avctx, AVFrame* frame); + virtual bool GetPicture(AVCodecContext* avctx, AVFrame* frame, DVDVideoPicture* picture); + virtual int Check (AVCodecContext* avctx); ++ virtual void Reset (); + virtual void Close(); + virtual const std::string Name() { return "vaapi"; } + virtual CCriticalSection* Section() { if(m_display) return m_display.get(); else return NULL; } ++ virtual bool CanSkipDeint() { return true; } + + int GetBuffer(AVCodecContext *avctx, AVFrame *pic); + void RelBuffer(AVCodecContext *avctx, AVFrame *pic); +-- +1.7.10.4 + + +From 58605e632bf6fdb9133e96d570951a36e65b5d97 Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Wed, 10 Apr 2013 11:26:01 +0200 +Subject: [PATCH 4/8] VAAPI: Use shared_ptr::unique() instead of use_count() + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp | 4 ++-- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index aedeb0f..114c8ce 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -207,8 +207,8 @@ int CDecoder::GetBuffer(AVCodecContext *avctx, AVFrame *pic) + else + { + // To avoid stutter, we scan the free surface pool (provided by decoder) for surfaces +- // that are 100% not in use by renderer or vpp. The pointers to these surfaces have a use_count of 1. +- for (; it != m_surfaces_free.end() && it->use_count() > 1; ++it) {} ++ // that are 100% not in use by renderer or vpp. The pointers to these surfaces are unique(use_count() == 1). ++ for (; it != m_surfaces_free.end() && !it->unique(); ++it) {} + + // If we have zero free surface from decoder OR all free surfaces are in use by renderer, we allocate a new surface + if (it == m_surfaces_free.end()) +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +index 47f98f8..f063b07 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +@@ -330,7 +330,7 @@ CSurfacePtr CVPP::getFreeSurface() + #ifdef VPP_AVAIL + for(int i = 0; i < m_bobTargetCount; ++i) + { +- if(m_bobTargets[i].use_count() == 1) ++ if(m_bobTargets[i].unique()) + { + return m_bobTargets[i]; + } +-- +1.7.10.4 + + +From 55c5d2d8da95a7cf968bcd5cbe858ab21e15f475 Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Wed, 10 Apr 2013 12:06:57 +0200 +Subject: [PATCH 5/8] VAAPI: Reduce VPP buffer size + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp | 15 +++++++++------ + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h | 2 ++ + 2 files changed, 11 insertions(+), 6 deletions(-) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index 114c8ce..4e27bb5 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -146,6 +146,9 @@ CSurfaceGL::~CSurfaceGL() + + CDecoder::CDecoder() + { ++ // Buffer size passed to VPP Init ++ m_buffer_size = 9; ++ + m_refs = 0; + m_surfaces_count = 0; + m_config = 0; +@@ -379,7 +382,7 @@ bool CDecoder::Open(AVCodecContext *avctx, enum PixelFormat fmt, unsigned int su + return false; + + m_vppth = new CVPPThread(m_display, avctx->width, avctx->height); +- m_vppth->Init(m_refs); // Ignore result, VPPThread just passes frames if init failed ++ m_vppth->Init(m_buffer_size); // Ignore result, VPPThread just passes frames if init failed + m_vppth->Start(); + + m_hwaccel->display = m_display->get(); +@@ -413,7 +416,7 @@ bool CDecoder::EnsureContext(AVCodecContext *avctx) + + int vpp_buf = 0; + if(CVPP::Supported()) +- vpp_buf = 4; ++ vpp_buf = m_buffer_size >> 1; + + return EnsureSurfaces(avctx, m_refs + m_renderbuffers_count + vpp_buf + 3); + } +@@ -503,7 +506,7 @@ int CDecoder::Decode(AVCodecContext* avctx, AVFrame* frame) + + int ret = 0; + +- if(m_vppth->GetInputQueueSize() < 4 && m_vppth->GetOutputQueueSize() < 8) ++ if(m_vppth->GetInputQueueSize() < (m_buffer_size >> 2) && m_vppth->GetOutputQueueSize() < (m_buffer_size >> 1)) + ret |= VC_BUFFER; + if(m_vppth->GetOutputQueueSize() > 0) + ret |= VC_PICTURE; +@@ -585,7 +588,7 @@ bool CVPPThread::Init(int num_refs) + if(!m_vpp->InitVpp()) + return false; + +- if(!m_vpp->InitDeintBob(num_refs + 16)) ++ if(!m_vpp->InitDeintBob(num_refs)) + return false; + + return true; +@@ -769,8 +772,8 @@ void CVPPThread::Process() + if(currentFrame.valid) + { + bool isInterlaced = currentFrame.DVDPic.iFlags & DVP_FLAG_INTERLACED; +- //if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) +- // isInterlaced = false; ++ if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) ++ isInterlaced = false; + + EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode; + EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod; +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +index 1aa2fd7..6ccef59 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +@@ -194,6 +194,8 @@ protected: + VASurfaceID m_surfaces[m_surfaces_max]; + unsigned m_renderbuffers_count; + ++ int m_buffer_size; ++ + int m_refs; + std::list m_surfaces_used; + std::list m_surfaces_free; +-- +1.7.10.4 + + +From b1d06507eaf2b765a36d8df3a4d527ac7618e153 Mon Sep 17 00:00:00 2001 +From: fritsch +Date: Thu, 25 Apr 2013 21:13:33 +0200 +Subject: [PATCH 6/8] VAAPI: adapt code to work with frodo + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index 4e27bb5..fbe3fd5 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -20,7 +20,6 @@ + #include "system.h" + #ifdef HAVE_LIBVA + #include "windowing/WindowingFactory.h" +-#include "settings/MediaSettings.h" + #include "settings/Settings.h" + #include "cores/dvdplayer/DVDClock.h" + #include "VAAPI.h" +@@ -775,8 +774,8 @@ void CVPPThread::Process() + if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) + isInterlaced = false; + +- EDEINTERLACEMODE mode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode; +- EINTERLACEMETHOD method = CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod; ++ EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; ++ EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; + + if (m_vpp->DeintBobReady() && (method == VS_INTERLACEMETHOD_VAAPI_AUTO || method == VS_INTERLACEMETHOD_AUTO) + && (mode == VS_DEINTERLACEMODE_FORCE || (mode == VS_DEINTERLACEMODE_AUTO && isInterlaced))) +-- +1.7.10.4 + + +From 754919b065c32fdf0e3b78a072cce6282d3c371c Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Sat, 27 Apr 2013 15:37:56 +0200 +Subject: [PATCH 7/8] VAAPI: Fix VPP compile if libva is too old + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +index f063b07..1fdad18 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +@@ -321,7 +321,7 @@ CSurfacePtr CVPP::DeintBob(const CSurfacePtr& input, bool topField) + + return bobTarget; + #else +- return VA_INVALID_ID; ++ return CSurfacePtr(); + #endif + } + +-- +1.7.10.4 + + +From b1c3a0320232080b935b3146146ba33ca6162f84 Mon Sep 17 00:00:00 2001 +From: BtbN +Date: Fri, 24 May 2013 19:25:59 +0200 +Subject: [PATCH 8/8] VAAPI, sqash me: Fix compile for new 1.2-testing branch + +--- + xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +index 1fdad18..8a500cc 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI_VPP.cpp +@@ -35,6 +35,11 @@ + + #include + ++#ifndef VA_SURFACE_ATTRIB_SETTABLE ++#define vaCreateSurfaces(d, f, w, h, s, ns, a, na) \ ++ vaCreateSurfaces(d, w, h, f, ns, s) ++#endif ++ + using namespace VAAPI; + + bool CVPP::isSupported = true; +-- +1.7.10.4 + diff --git a/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.12-enable-vpp-fix-audio-out-of-sync.patch b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.12-enable-vpp-fix-audio-out-of-sync.patch new file mode 100644 index 0000000000..95190cbb52 --- /dev/null +++ b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.12-enable-vpp-fix-audio-out-of-sync.patch @@ -0,0 +1,34 @@ +commit 7205cbc5abda0a8571170a132bd30fc54a4aa6b6 +Author: fritsch +Date: Tue Jun 18 18:59:05 2013 +0200 + + VPP: SkipDeint was not only meant for interlaced content (fixes Audio out of sync after pause, ffwd) + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index fbe3fd5..0f138aa 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -771,8 +771,8 @@ void CVPPThread::Process() + if(currentFrame.valid) + { + bool isInterlaced = currentFrame.DVDPic.iFlags & DVP_FLAG_INTERLACED; +- if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) +- isInterlaced = false; ++ //if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) ++ // isInterlaced = false; + + EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; + EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +index 6ccef59..b7b82c9 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +@@ -181,7 +181,7 @@ public: + virtual void Close(); + virtual const std::string Name() { return "vaapi"; } + virtual CCriticalSection* Section() { if(m_display) return m_display.get(); else return NULL; } +- virtual bool CanSkipDeint() { return true; } ++ virtual bool CanSkipDeint() { return false; } + + int GetBuffer(AVCodecContext *avctx, AVFrame *pic); + void RelBuffer(AVCodecContext *avctx, AVFrame *pic); diff --git a/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.13-vpp-fix-skipping.patch b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.13-vpp-fix-skipping.patch new file mode 100644 index 0000000000..b617367c5d --- /dev/null +++ b/packages/mediacenter/xbmc/patches/12.2.0/xbmc-995.13-vpp-fix-skipping.patch @@ -0,0 +1,95 @@ +commit 6f1d11cf3e44a37b5d90689bda9fc7df11ae8a52 +Author: fritsch +Date: Wed Jun 19 20:30:44 2013 +0200 + + VPP: Implement proper deinterlace skipping + +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +index 0f138aa..fcc9ec9 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.cpp +@@ -569,10 +569,15 @@ int CDecoder::Check(AVCodecContext* avctx) + return 0; + } + ++bool CDecoder::CanSkipDeint() ++{ ++ return m_vppth->CanSkipDeint(); ++} + + CVPPThread::CVPPThread(CDisplayPtr& display, int width, int height) + :CThread("VAAPI VPP Thread") + ,m_stop(false) ++ ,m_skipDeinterlace(false) + { + m_vpp = new CVPP(display, width, height); + } +@@ -720,6 +725,11 @@ int CVPPThread::GetOutputQueueSize() + return m_output_queue.size(); + } + ++bool CVPPThread::CanSkipDeint() ++{ ++ return m_skipDeinterlace; ++} ++ + void CVPPThread::Flush() + { + CSingleLock lock(m_work_lock); +@@ -771,22 +781,22 @@ void CVPPThread::Process() + if(currentFrame.valid) + { + bool isInterlaced = currentFrame.DVDPic.iFlags & DVP_FLAG_INTERLACED; +- //if(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) +- // isInterlaced = false; + + EDEINTERLACEMODE mode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; + EINTERLACEMETHOD method = g_settings.m_currentVideoSettings.m_InterlaceMethod; + +- if (m_vpp->DeintBobReady() && (method == VS_INTERLACEMETHOD_VAAPI_AUTO || method == VS_INTERLACEMETHOD_AUTO) +- && (mode == VS_DEINTERLACEMODE_FORCE || (mode == VS_DEINTERLACEMODE_AUTO && isInterlaced))) ++ if (m_vpp->DeintBobReady() && !(currentFrame.DVDPic.iFlags & DVP_FLAG_DROPDEINT) ++ && (method == VS_INTERLACEMETHOD_VAAPI_AUTO || method == VS_INTERLACEMETHOD_AUTO) ++ && (mode == VS_DEINTERLACEMODE_FORCE || (mode == VS_DEINTERLACEMODE_AUTO && isInterlaced))) + { + bool topField = currentFrame.DVDPic.iFlags & DVP_FLAG_TOP_FIELD_FIRST; +- ++ m_skipDeinterlace = true; + DoDeinterlacing(currentFrame, topField); + DoDeinterlacing(currentFrame, !topField); + } + else + { ++ m_skipDeinterlace = false; + CVPPRenderPicture res; + res.valid = true; + res.DVDPic = currentFrame.DVDPic; +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +index b7b82c9..8361ff0 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/VAAPI.h +@@ -134,6 +134,7 @@ public: + + void InsertNewFrame(CVPPDecodedPicture &new_frame); + void WaitForOutput(unsigned long msec = 0); ++ virtual bool CanSkipDeint(); + CVPPRenderPicture GetOutputPicture(); + + int GetInputQueueSize(); +@@ -153,6 +154,7 @@ protected: + CVPP *m_vpp; + + bool m_stop; ++ bool m_skipDeinterlace; + + CCriticalSection m_work_lock; + +@@ -181,7 +183,7 @@ public: + virtual void Close(); + virtual const std::string Name() { return "vaapi"; } + virtual CCriticalSection* Section() { if(m_display) return m_display.get(); else return NULL; } +- virtual bool CanSkipDeint() { return false; } ++ virtual bool CanSkipDeint(); + + int GetBuffer(AVCodecContext *avctx, AVFrame *pic); + void RelBuffer(AVCodecContext *avctx, AVFrame *pic); From 5b8ee3035c00aae4c16fdd3cabea7e7e778a916a Mon Sep 17 00:00:00 2001 From: Henk Wiedig Date: Fri, 21 Jun 2013 05:53:13 +0200 Subject: [PATCH 15/15] xbmc-addon-xvdr: add pvr api 1.8.0 patch (xvdr pull #111) --- .../xbmc-addon-xvdr-pvr-api-1-8-0.patch | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 packages/mediacenter/xbmc-addon-xvdr/patches/871faab/xbmc-addon-xvdr-pvr-api-1-8-0.patch diff --git a/packages/mediacenter/xbmc-addon-xvdr/patches/871faab/xbmc-addon-xvdr-pvr-api-1-8-0.patch b/packages/mediacenter/xbmc-addon-xvdr/patches/871faab/xbmc-addon-xvdr-pvr-api-1-8-0.patch new file mode 100644 index 0000000000..8ebb045861 --- /dev/null +++ b/packages/mediacenter/xbmc-addon-xvdr/patches/871faab/xbmc-addon-xvdr-pvr-api-1-8-0.patch @@ -0,0 +1,159 @@ +From b0dd079bee34969dade531ec33bc933cf5565964 Mon Sep 17 00:00:00 2001 +From: Daniel Meyerholt +Date: Wed, 19 Jun 2013 18:55:54 +0200 +Subject: [PATCH] Update xbmc addon headers + +--- + src/xvdr/XBMCAddon.cpp | 2 +- + src/xvdr/include/DVDDemuxPacket.h | 2 +- + src/xvdr/include/xbmc_addon_dll.h | 2 +- + src/xvdr/include/xbmc_epg_types.h | 2 +- + src/xvdr/include/xbmc_pvr_dll.h | 3 ++- + src/xvdr/include/xbmc_pvr_types.h | 25 ++++++++++++++++++++----- + 6 files changed, 26 insertions(+), 10 deletions(-) + +diff --git a/src/xvdr/XBMCAddon.cpp b/src/xvdr/XBMCAddon.cpp +index 60bfbe8..23c89a0 100644 +--- a/src/xvdr/XBMCAddon.cpp ++++ b/src/xvdr/XBMCAddon.cpp +@@ -828,7 +828,7 @@ int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) + return mClient->GetRecordingLastPosition(recording.strRecordingId); + } + +-PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook) { ++PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook, const PVR_MENUHOOK_DATA &item) { + switch(menuhook.iHookId) { + case XVDR_HOOK_SETTINGS_CHANNELSCAN: + DialogChannelScan(); +diff --git a/src/xvdr/include/DVDDemuxPacket.h b/src/xvdr/include/DVDDemuxPacket.h +index 7d9967e..cf3c1e0 100644 +--- a/src/xvdr/include/DVDDemuxPacket.h ++++ b/src/xvdr/include/DVDDemuxPacket.h +@@ -1,7 +1,7 @@ + #pragma once + + /* +- * Copyright (C) 2012 Team XBMC ++ * Copyright (C) 2012-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify +diff --git a/src/xvdr/include/xbmc_addon_dll.h b/src/xvdr/include/xbmc_addon_dll.h +index 9402623..12d3d91 100644 +--- a/src/xvdr/include/xbmc_addon_dll.h ++++ b/src/xvdr/include/xbmc_addon_dll.h +@@ -21,7 +21,7 @@ + * + */ + +-#ifdef _WIN32 ++#ifdef TARGET_WINDOWS + #include + #else + #ifndef __cdecl +diff --git a/src/xvdr/include/xbmc_epg_types.h b/src/xvdr/include/xbmc_epg_types.h +index 2284dda..c486be4 100644 +--- a/src/xvdr/include/xbmc_epg_types.h ++++ b/src/xvdr/include/xbmc_epg_types.h +@@ -26,7 +26,7 @@ + #undef PRAGMA_PACK_BEGIN + #undef PRAGMA_PACK_END + +-#if defined(__GNUC__) && !defined(__MINGW32__) ++#if defined(__GNUC__) + #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) + #define ATTRIBUTE_PACKED __attribute__ ((packed)) + #define PRAGMA_PACK 0 +diff --git a/src/xvdr/include/xbmc_pvr_dll.h b/src/xvdr/include/xbmc_pvr_dll.h +index a40022b..5ef7b95 100644 +--- a/src/xvdr/include/xbmc_pvr_dll.h ++++ b/src/xvdr/include/xbmc_pvr_dll.h +@@ -108,10 +108,11 @@ + * 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); ++ PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA &item); + //@} + + /*! @name PVR EPG methods +diff --git a/src/xvdr/include/xbmc_pvr_types.h b/src/xvdr/include/xbmc_pvr_types.h +index a8193ed..1fb0c6c 100644 +--- a/src/xvdr/include/xbmc_pvr_types.h ++++ b/src/xvdr/include/xbmc_pvr_types.h +@@ -22,7 +22,7 @@ + #ifndef __PVRCLIENT_TYPES_H__ + #define __PVRCLIENT_TYPES_H__ + +-#ifdef _WIN32 ++#ifdef TARGET_WINDOWS + #include + #else + #ifndef __cdecl +@@ -52,7 +52,7 @@ + #undef PRAGMA_PACK_BEGIN + #undef PRAGMA_PACK_END + +-#if defined(__GNUC__) && !defined(__MINGW32__) ++#if defined(__GNUC__) + #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) + #define ATTRIBUTE_PACKED __attribute__ ((packed)) + #define PRAGMA_PACK 0 +@@ -74,10 +74,10 @@ + #define PVR_STREAM_MAX_STREAMS 20 + + /* current PVR API version */ +-#define XBMC_PVR_API_VERSION "1.7.0" ++#define XBMC_PVR_API_VERSION "1.8.0" + + /* min. PVR API version */ +-#define XBMC_PVR_MIN_API_VERSION "1.7.0" ++#define XBMC_PVR_MIN_API_VERSION "1.8.0" + + #ifdef __cplusplus + extern "C" { +@@ -121,6 +121,7 @@ + */ + 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 */ +@@ -310,6 +311,20 @@ + } 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 +@@ -324,7 +339,7 @@ + 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&); ++ 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); +-- +1.8.1.6 +